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/cdefs.h> 227453645fSAndriy Voskoboinyk __FBSDID("$FreeBSD$"); 237453645fSAndriy Voskoboinyk 247453645fSAndriy Voskoboinyk #include <sys/param.h> 257453645fSAndriy Voskoboinyk #include <sys/sysctl.h> 267453645fSAndriy Voskoboinyk #include <sys/lock.h> 277453645fSAndriy Voskoboinyk #include <sys/mutex.h> 287453645fSAndriy Voskoboinyk #include <sys/mbuf.h> 297453645fSAndriy Voskoboinyk #include <sys/kernel.h> 307453645fSAndriy Voskoboinyk #include <sys/socket.h> 317453645fSAndriy Voskoboinyk #include <sys/systm.h> 327453645fSAndriy Voskoboinyk #include <sys/malloc.h> 337453645fSAndriy Voskoboinyk #include <sys/module.h> 347453645fSAndriy Voskoboinyk #include <sys/bus.h> 357453645fSAndriy Voskoboinyk #include <sys/endian.h> 367453645fSAndriy Voskoboinyk #include <sys/linker.h> 377453645fSAndriy Voskoboinyk #include <sys/kdb.h> 387453645fSAndriy Voskoboinyk 397453645fSAndriy Voskoboinyk #include <machine/bus.h> 407453645fSAndriy Voskoboinyk #include <machine/resource.h> 417453645fSAndriy Voskoboinyk #include <sys/rman.h> 427453645fSAndriy Voskoboinyk 437453645fSAndriy Voskoboinyk #include <dev/pci/pcireg.h> 447453645fSAndriy Voskoboinyk #include <dev/pci/pcivar.h> 457453645fSAndriy Voskoboinyk 467453645fSAndriy Voskoboinyk #include <net/if.h> 477453645fSAndriy Voskoboinyk #include <net/ethernet.h> 487453645fSAndriy Voskoboinyk #include <net/if_media.h> 497453645fSAndriy Voskoboinyk 507453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h> 517453645fSAndriy Voskoboinyk 52519e6c0fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h> 537453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h> 547453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_nop.h> 557453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h> 567453645fSAndriy Voskoboinyk 577453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_var.h> 587453645fSAndriy Voskoboinyk 597453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_attach.h> 607453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_reg.h> 617453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_rx.h> 627453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_tx.h> 637453645fSAndriy Voskoboinyk 647453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/pci/r92ce_reg.h> 657453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> 667453645fSAndriy Voskoboinyk 677453645fSAndriy Voskoboinyk 687453645fSAndriy Voskoboinyk static device_probe_t rtwn_pci_probe; 697453645fSAndriy Voskoboinyk static device_attach_t rtwn_pci_attach; 707453645fSAndriy Voskoboinyk static device_detach_t rtwn_pci_detach; 717453645fSAndriy Voskoboinyk static device_shutdown_t rtwn_pci_shutdown; 727453645fSAndriy Voskoboinyk static device_suspend_t rtwn_pci_suspend; 737453645fSAndriy Voskoboinyk static device_resume_t rtwn_pci_resume; 747453645fSAndriy Voskoboinyk 757453645fSAndriy Voskoboinyk static int rtwn_pci_alloc_rx_list(struct rtwn_softc *); 767453645fSAndriy Voskoboinyk static void rtwn_pci_reset_rx_list(struct rtwn_softc *); 777453645fSAndriy Voskoboinyk static void rtwn_pci_free_rx_list(struct rtwn_softc *); 787453645fSAndriy Voskoboinyk static int rtwn_pci_alloc_tx_list(struct rtwn_softc *, int); 79519e6c0fSAndriy Voskoboinyk static void rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *, int); 80519e6c0fSAndriy Voskoboinyk static void rtwn_pci_reset_beacon_ring(struct rtwn_softc *, int); 817453645fSAndriy Voskoboinyk static void rtwn_pci_reset_tx_list(struct rtwn_softc *, 827453645fSAndriy Voskoboinyk struct ieee80211vap *, int); 837453645fSAndriy Voskoboinyk static void rtwn_pci_free_tx_list(struct rtwn_softc *, int); 847453645fSAndriy Voskoboinyk static void rtwn_pci_reset_lists(struct rtwn_softc *, 857453645fSAndriy Voskoboinyk struct ieee80211vap *); 867453645fSAndriy Voskoboinyk static int rtwn_pci_fw_write_block(struct rtwn_softc *, 877453645fSAndriy Voskoboinyk const uint8_t *, uint16_t, int); 887453645fSAndriy Voskoboinyk static uint16_t rtwn_pci_get_qmap(struct rtwn_softc *); 897453645fSAndriy Voskoboinyk static void rtwn_pci_set_desc_addr(struct rtwn_softc *); 90d067ef0fSAndriy Voskoboinyk static void rtwn_pci_beacon_update_begin(struct rtwn_softc *, 91d067ef0fSAndriy Voskoboinyk struct ieee80211vap *); 92d067ef0fSAndriy Voskoboinyk static void rtwn_pci_beacon_update_end(struct rtwn_softc *, 93d067ef0fSAndriy Voskoboinyk struct ieee80211vap *); 947453645fSAndriy Voskoboinyk static void rtwn_pci_attach_methods(struct rtwn_softc *); 957453645fSAndriy Voskoboinyk 967453645fSAndriy Voskoboinyk 97*ae60d856SAndriy Voskoboinyk static const struct rtwn_pci_ident * 98*ae60d856SAndriy Voskoboinyk rtwn_pci_probe_sub(device_t dev) 99*ae60d856SAndriy Voskoboinyk { 100*ae60d856SAndriy Voskoboinyk const struct rtwn_pci_ident *ident; 101*ae60d856SAndriy Voskoboinyk int vendor_id, device_id; 102*ae60d856SAndriy Voskoboinyk 103*ae60d856SAndriy Voskoboinyk vendor_id = pci_get_vendor(dev); 104*ae60d856SAndriy Voskoboinyk device_id = pci_get_device(dev); 105*ae60d856SAndriy Voskoboinyk 106*ae60d856SAndriy Voskoboinyk for (ident = rtwn_pci_ident_table; ident->name != NULL; ident++) 107*ae60d856SAndriy Voskoboinyk if (vendor_id == ident->vendor && device_id == ident->device) 108*ae60d856SAndriy Voskoboinyk return (ident); 109*ae60d856SAndriy Voskoboinyk 110*ae60d856SAndriy Voskoboinyk return (NULL); 111*ae60d856SAndriy Voskoboinyk } 1127453645fSAndriy Voskoboinyk 1137453645fSAndriy Voskoboinyk static int 1147453645fSAndriy Voskoboinyk rtwn_pci_probe(device_t dev) 1157453645fSAndriy Voskoboinyk { 1167453645fSAndriy Voskoboinyk const struct rtwn_pci_ident *ident; 1177453645fSAndriy Voskoboinyk 118*ae60d856SAndriy Voskoboinyk ident = rtwn_pci_probe_sub(dev); 119*ae60d856SAndriy Voskoboinyk if (ident != NULL) { 1207453645fSAndriy Voskoboinyk device_set_desc(dev, ident->name); 1217453645fSAndriy Voskoboinyk return (BUS_PROBE_DEFAULT); 1227453645fSAndriy Voskoboinyk } 1237453645fSAndriy Voskoboinyk return (ENXIO); 1247453645fSAndriy Voskoboinyk } 1257453645fSAndriy Voskoboinyk 1267453645fSAndriy Voskoboinyk static int 1277453645fSAndriy Voskoboinyk rtwn_pci_alloc_rx_list(struct rtwn_softc *sc) 1287453645fSAndriy Voskoboinyk { 1297453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 1307453645fSAndriy Voskoboinyk struct rtwn_rx_ring *rx_ring = &pc->rx_ring; 1317453645fSAndriy Voskoboinyk struct rtwn_rx_data *rx_data; 1327453645fSAndriy Voskoboinyk bus_size_t size; 1337453645fSAndriy Voskoboinyk int i, error; 1347453645fSAndriy Voskoboinyk 1357453645fSAndriy Voskoboinyk /* Allocate Rx descriptors. */ 1367453645fSAndriy Voskoboinyk size = sizeof(struct r92ce_rx_stat) * RTWN_PCI_RX_LIST_COUNT; 1377453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1387453645fSAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 1397453645fSAndriy Voskoboinyk size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat); 1407453645fSAndriy Voskoboinyk if (error != 0) { 1417453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create rx desc DMA tag\n"); 1427453645fSAndriy Voskoboinyk goto fail; 1437453645fSAndriy Voskoboinyk } 1447453645fSAndriy Voskoboinyk 1457453645fSAndriy Voskoboinyk error = bus_dmamem_alloc(rx_ring->desc_dmat, (void **)&rx_ring->desc, 1467453645fSAndriy Voskoboinyk BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1477453645fSAndriy Voskoboinyk &rx_ring->desc_map); 1487453645fSAndriy Voskoboinyk if (error != 0) { 1497453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not allocate rx desc\n"); 1507453645fSAndriy Voskoboinyk goto fail; 1517453645fSAndriy Voskoboinyk } 1527453645fSAndriy Voskoboinyk error = bus_dmamap_load(rx_ring->desc_dmat, rx_ring->desc_map, 1537453645fSAndriy Voskoboinyk rx_ring->desc, size, rtwn_pci_dma_map_addr, &rx_ring->paddr, 0); 1547453645fSAndriy Voskoboinyk if (error != 0) { 1557453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not load rx desc DMA map\n"); 1567453645fSAndriy Voskoboinyk goto fail; 1577453645fSAndriy Voskoboinyk } 1587453645fSAndriy Voskoboinyk bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map, 1597453645fSAndriy Voskoboinyk BUS_DMASYNC_PREWRITE); 1607453645fSAndriy Voskoboinyk 1617453645fSAndriy Voskoboinyk /* Create RX buffer DMA tag. */ 1627453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 163a14f9888SAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 164a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &rx_ring->data_dmat); 1657453645fSAndriy Voskoboinyk if (error != 0) { 1667453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); 1677453645fSAndriy Voskoboinyk goto fail; 1687453645fSAndriy Voskoboinyk } 1697453645fSAndriy Voskoboinyk 1707453645fSAndriy Voskoboinyk /* Allocate Rx buffers. */ 1717453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { 1727453645fSAndriy Voskoboinyk rx_data = &rx_ring->rx_data[i]; 1737453645fSAndriy Voskoboinyk error = bus_dmamap_create(rx_ring->data_dmat, 0, &rx_data->map); 1747453645fSAndriy Voskoboinyk if (error != 0) { 1757453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1767453645fSAndriy Voskoboinyk "could not create rx buf DMA map\n"); 1777453645fSAndriy Voskoboinyk goto fail; 1787453645fSAndriy Voskoboinyk } 1797453645fSAndriy Voskoboinyk 180a14f9888SAndriy Voskoboinyk rx_data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, 181a14f9888SAndriy Voskoboinyk MJUMPAGESIZE); 1827453645fSAndriy Voskoboinyk if (rx_data->m == NULL) { 1837453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1847453645fSAndriy Voskoboinyk "could not allocate rx mbuf\n"); 1857453645fSAndriy Voskoboinyk error = ENOMEM; 1867453645fSAndriy Voskoboinyk goto fail; 1877453645fSAndriy Voskoboinyk } 1887453645fSAndriy Voskoboinyk 1897453645fSAndriy Voskoboinyk error = bus_dmamap_load(rx_ring->data_dmat, rx_data->map, 190a14f9888SAndriy Voskoboinyk mtod(rx_data->m, void *), MJUMPAGESIZE, 191a14f9888SAndriy Voskoboinyk rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT); 1927453645fSAndriy Voskoboinyk if (error != 0) { 1937453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1947453645fSAndriy Voskoboinyk "could not load rx buf DMA map"); 1957453645fSAndriy Voskoboinyk goto fail; 1967453645fSAndriy Voskoboinyk } 1977453645fSAndriy Voskoboinyk 1987453645fSAndriy Voskoboinyk rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], rx_data->paddr, 199a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, i); 2007453645fSAndriy Voskoboinyk } 2017453645fSAndriy Voskoboinyk rx_ring->cur = 0; 2027453645fSAndriy Voskoboinyk 2037453645fSAndriy Voskoboinyk return (0); 2047453645fSAndriy Voskoboinyk 2057453645fSAndriy Voskoboinyk fail: 2067453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(sc); 2077453645fSAndriy Voskoboinyk return (error); 2087453645fSAndriy Voskoboinyk } 2097453645fSAndriy Voskoboinyk 2107453645fSAndriy Voskoboinyk static void 2117453645fSAndriy Voskoboinyk rtwn_pci_reset_rx_list(struct rtwn_softc *sc) 2127453645fSAndriy Voskoboinyk { 2137453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 2147453645fSAndriy Voskoboinyk struct rtwn_rx_ring *rx_ring = &pc->rx_ring; 2157453645fSAndriy Voskoboinyk struct rtwn_rx_data *rx_data; 2167453645fSAndriy Voskoboinyk int i; 2177453645fSAndriy Voskoboinyk 2187453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { 2197453645fSAndriy Voskoboinyk rx_data = &rx_ring->rx_data[i]; 2207453645fSAndriy Voskoboinyk rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], 221a14f9888SAndriy Voskoboinyk rx_data->paddr, MJUMPAGESIZE, i); 2227453645fSAndriy Voskoboinyk } 2237453645fSAndriy Voskoboinyk rx_ring->cur = 0; 2247453645fSAndriy Voskoboinyk } 2257453645fSAndriy Voskoboinyk 2267453645fSAndriy Voskoboinyk static void 2277453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(struct rtwn_softc *sc) 2287453645fSAndriy Voskoboinyk { 2297453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 2307453645fSAndriy Voskoboinyk struct rtwn_rx_ring *rx_ring = &pc->rx_ring; 2317453645fSAndriy Voskoboinyk struct rtwn_rx_data *rx_data; 2327453645fSAndriy Voskoboinyk int i; 2337453645fSAndriy Voskoboinyk 2347453645fSAndriy Voskoboinyk if (rx_ring->desc_dmat != NULL) { 2357453645fSAndriy Voskoboinyk if (rx_ring->desc != NULL) { 2367453645fSAndriy Voskoboinyk bus_dmamap_sync(rx_ring->desc_dmat, 2377453645fSAndriy Voskoboinyk rx_ring->desc_map, 2387453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2397453645fSAndriy Voskoboinyk bus_dmamap_unload(rx_ring->desc_dmat, 2407453645fSAndriy Voskoboinyk rx_ring->desc_map); 2417453645fSAndriy Voskoboinyk bus_dmamem_free(rx_ring->desc_dmat, rx_ring->desc, 2427453645fSAndriy Voskoboinyk rx_ring->desc_map); 2437453645fSAndriy Voskoboinyk rx_ring->desc = NULL; 2447453645fSAndriy Voskoboinyk } 2457453645fSAndriy Voskoboinyk bus_dma_tag_destroy(rx_ring->desc_dmat); 2467453645fSAndriy Voskoboinyk rx_ring->desc_dmat = NULL; 2477453645fSAndriy Voskoboinyk } 2487453645fSAndriy Voskoboinyk 2497453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { 2507453645fSAndriy Voskoboinyk rx_data = &rx_ring->rx_data[i]; 2517453645fSAndriy Voskoboinyk 2527453645fSAndriy Voskoboinyk if (rx_data->m != NULL) { 2537453645fSAndriy Voskoboinyk bus_dmamap_sync(rx_ring->data_dmat, 2547453645fSAndriy Voskoboinyk rx_data->map, BUS_DMASYNC_POSTREAD); 2557453645fSAndriy Voskoboinyk bus_dmamap_unload(rx_ring->data_dmat, rx_data->map); 2567453645fSAndriy Voskoboinyk m_freem(rx_data->m); 2577453645fSAndriy Voskoboinyk rx_data->m = NULL; 2587453645fSAndriy Voskoboinyk } 2597453645fSAndriy Voskoboinyk bus_dmamap_destroy(rx_ring->data_dmat, rx_data->map); 2607453645fSAndriy Voskoboinyk rx_data->map = NULL; 2617453645fSAndriy Voskoboinyk } 2627453645fSAndriy Voskoboinyk if (rx_ring->data_dmat != NULL) { 2637453645fSAndriy Voskoboinyk bus_dma_tag_destroy(rx_ring->data_dmat); 2647453645fSAndriy Voskoboinyk rx_ring->data_dmat = NULL; 2657453645fSAndriy Voskoboinyk } 2667453645fSAndriy Voskoboinyk } 2677453645fSAndriy Voskoboinyk 2687453645fSAndriy Voskoboinyk static int 2697453645fSAndriy Voskoboinyk rtwn_pci_alloc_tx_list(struct rtwn_softc *sc, int qid) 2707453645fSAndriy Voskoboinyk { 2717453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 2727453645fSAndriy Voskoboinyk struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; 2737453645fSAndriy Voskoboinyk bus_size_t size; 2747453645fSAndriy Voskoboinyk int i, error; 2757453645fSAndriy Voskoboinyk 2767453645fSAndriy Voskoboinyk size = sc->txdesc_len * RTWN_PCI_TX_LIST_COUNT; 2777453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), PAGE_SIZE, 0, 2787453645fSAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2797453645fSAndriy Voskoboinyk size, 1, size, 0, NULL, NULL, &tx_ring->desc_dmat); 2807453645fSAndriy Voskoboinyk if (error != 0) { 2817453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); 2827453645fSAndriy Voskoboinyk goto fail; 2837453645fSAndriy Voskoboinyk } 2847453645fSAndriy Voskoboinyk 2857453645fSAndriy Voskoboinyk error = bus_dmamem_alloc(tx_ring->desc_dmat, &tx_ring->desc, 2867453645fSAndriy Voskoboinyk BUS_DMA_NOWAIT | BUS_DMA_ZERO, &tx_ring->desc_map); 2877453645fSAndriy Voskoboinyk if (error != 0) { 2887453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "can't map tx ring DMA memory\n"); 2897453645fSAndriy Voskoboinyk goto fail; 2907453645fSAndriy Voskoboinyk } 2917453645fSAndriy Voskoboinyk error = bus_dmamap_load(tx_ring->desc_dmat, tx_ring->desc_map, 2927453645fSAndriy Voskoboinyk tx_ring->desc, size, rtwn_pci_dma_map_addr, &tx_ring->paddr, 2937453645fSAndriy Voskoboinyk BUS_DMA_NOWAIT); 2947453645fSAndriy Voskoboinyk if (error != 0) { 2957453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not load desc DMA map\n"); 2967453645fSAndriy Voskoboinyk goto fail; 2977453645fSAndriy Voskoboinyk } 2987453645fSAndriy Voskoboinyk bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, 2997453645fSAndriy Voskoboinyk BUS_DMASYNC_PREWRITE); 3007453645fSAndriy Voskoboinyk 3017453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 302a14f9888SAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 303a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &tx_ring->data_dmat); 3047453645fSAndriy Voskoboinyk if (error != 0) { 3057453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); 3067453645fSAndriy Voskoboinyk goto fail; 3077453645fSAndriy Voskoboinyk } 3087453645fSAndriy Voskoboinyk 3097453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 3107453645fSAndriy Voskoboinyk struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; 3117453645fSAndriy Voskoboinyk void *tx_desc = (uint8_t *)tx_ring->desc + sc->txdesc_len * i; 3127453645fSAndriy Voskoboinyk uint32_t next_desc_addr = tx_ring->paddr + 3137453645fSAndriy Voskoboinyk sc->txdesc_len * ((i + 1) % RTWN_PCI_TX_LIST_COUNT); 3147453645fSAndriy Voskoboinyk 3157453645fSAndriy Voskoboinyk rtwn_pci_setup_tx_desc(pc, tx_desc, next_desc_addr); 3167453645fSAndriy Voskoboinyk 3177453645fSAndriy Voskoboinyk error = bus_dmamap_create(tx_ring->data_dmat, 0, &tx_data->map); 3187453645fSAndriy Voskoboinyk if (error != 0) { 3197453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 3207453645fSAndriy Voskoboinyk "could not create tx buf DMA map\n"); 3217453645fSAndriy Voskoboinyk return (error); 3227453645fSAndriy Voskoboinyk } 3237453645fSAndriy Voskoboinyk tx_data->m = NULL; 3247453645fSAndriy Voskoboinyk tx_data->ni = NULL; 3257453645fSAndriy Voskoboinyk } 3267453645fSAndriy Voskoboinyk return (0); 3277453645fSAndriy Voskoboinyk 3287453645fSAndriy Voskoboinyk fail: 3297453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(sc, qid); 3307453645fSAndriy Voskoboinyk return (error); 3317453645fSAndriy Voskoboinyk } 3327453645fSAndriy Voskoboinyk 3337453645fSAndriy Voskoboinyk static void 334519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *sc, int qid) 3357453645fSAndriy Voskoboinyk { 3367453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 337519e6c0fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; 338519e6c0fSAndriy Voskoboinyk int i; 3397453645fSAndriy Voskoboinyk 3407453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 341519e6c0fSAndriy Voskoboinyk struct rtwn_tx_data *data = &ring->tx_data[i]; 342519e6c0fSAndriy Voskoboinyk void *desc = (uint8_t *)ring->desc + sc->txdesc_len * i; 3437453645fSAndriy Voskoboinyk 344519e6c0fSAndriy Voskoboinyk rtwn_pci_copy_tx_desc(pc, desc, NULL); 3457453645fSAndriy Voskoboinyk 346519e6c0fSAndriy Voskoboinyk if (data->m != NULL) { 347519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, data->map, 348519e6c0fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 349519e6c0fSAndriy Voskoboinyk bus_dmamap_unload(ring->data_dmat, data->map); 350519e6c0fSAndriy Voskoboinyk m_freem(data->m); 351519e6c0fSAndriy Voskoboinyk data->m = NULL; 3527453645fSAndriy Voskoboinyk } 353519e6c0fSAndriy Voskoboinyk if (data->ni != NULL) { 354519e6c0fSAndriy Voskoboinyk ieee80211_free_node(data->ni); 355519e6c0fSAndriy Voskoboinyk data->ni = NULL; 3567453645fSAndriy Voskoboinyk } 3577453645fSAndriy Voskoboinyk } 3587453645fSAndriy Voskoboinyk 359519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 3607453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 3617453645fSAndriy Voskoboinyk 3627453645fSAndriy Voskoboinyk sc->qfullmsk &= ~(1 << qid); 363519e6c0fSAndriy Voskoboinyk ring->queued = 0; 364519e6c0fSAndriy Voskoboinyk ring->last = ring->cur = 0; 365519e6c0fSAndriy Voskoboinyk } 366519e6c0fSAndriy Voskoboinyk 367519e6c0fSAndriy Voskoboinyk /* 368519e6c0fSAndriy Voskoboinyk * Clear entry 0 (or 1) in the beacon queue (other are not used). 369519e6c0fSAndriy Voskoboinyk */ 370519e6c0fSAndriy Voskoboinyk static void 371519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(struct rtwn_softc *sc, int id) 372519e6c0fSAndriy Voskoboinyk { 373519e6c0fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 374519e6c0fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE]; 375519e6c0fSAndriy Voskoboinyk struct rtwn_tx_data *data = &ring->tx_data[id]; 376519e6c0fSAndriy Voskoboinyk struct rtwn_tx_desc_common *txd = (struct rtwn_tx_desc_common *) 377519e6c0fSAndriy Voskoboinyk ((uint8_t *)ring->desc + id * sc->txdesc_len); 378519e6c0fSAndriy Voskoboinyk 379519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); 380519e6c0fSAndriy Voskoboinyk if (txd->flags0 & RTWN_FLAGS0_OWN) { 381519e6c0fSAndriy Voskoboinyk /* Clear OWN bit. */ 382519e6c0fSAndriy Voskoboinyk txd->flags0 &= ~RTWN_FLAGS0_OWN; 383519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 384519e6c0fSAndriy Voskoboinyk BUS_DMASYNC_PREWRITE); 385519e6c0fSAndriy Voskoboinyk 386519e6c0fSAndriy Voskoboinyk /* Unload mbuf. */ 387519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, data->map, 388519e6c0fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 389519e6c0fSAndriy Voskoboinyk bus_dmamap_unload(ring->data_dmat, data->map); 390519e6c0fSAndriy Voskoboinyk } 391519e6c0fSAndriy Voskoboinyk } 392519e6c0fSAndriy Voskoboinyk 393519e6c0fSAndriy Voskoboinyk /* 394519e6c0fSAndriy Voskoboinyk * Drop stale entries from Tx ring before the vap will be deleted. 395519e6c0fSAndriy Voskoboinyk * In case if vap is NULL just free everything and reset cur / last pointers. 396519e6c0fSAndriy Voskoboinyk */ 397519e6c0fSAndriy Voskoboinyk static void 398519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_list(struct rtwn_softc *sc, struct ieee80211vap *vap, 399519e6c0fSAndriy Voskoboinyk int qid) 400519e6c0fSAndriy Voskoboinyk { 401519e6c0fSAndriy Voskoboinyk int i; 402519e6c0fSAndriy Voskoboinyk 403519e6c0fSAndriy Voskoboinyk if (vap == NULL) { 404519e6c0fSAndriy Voskoboinyk if (qid != RTWN_PCI_BEACON_QUEUE) { 405519e6c0fSAndriy Voskoboinyk /* 406519e6c0fSAndriy Voskoboinyk * Device was stopped; just clear all entries. 407519e6c0fSAndriy Voskoboinyk */ 408519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_ring_stopped(sc, qid); 409519e6c0fSAndriy Voskoboinyk } else { 410519e6c0fSAndriy Voskoboinyk for (i = 0; i < RTWN_PORT_COUNT; i++) 411519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(sc, i); 412519e6c0fSAndriy Voskoboinyk } 413519e6c0fSAndriy Voskoboinyk } else if (qid == RTWN_PCI_BEACON_QUEUE && 414519e6c0fSAndriy Voskoboinyk (vap->iv_opmode == IEEE80211_M_HOSTAP || 415519e6c0fSAndriy Voskoboinyk vap->iv_opmode == IEEE80211_M_IBSS)) { 416519e6c0fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 417519e6c0fSAndriy Voskoboinyk 418519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(sc, uvp->id); 419519e6c0fSAndriy Voskoboinyk } else { 420519e6c0fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 421519e6c0fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; 422519e6c0fSAndriy Voskoboinyk 423519e6c0fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 424519e6c0fSAndriy Voskoboinyk struct rtwn_tx_data *data = &ring->tx_data[i]; 425519e6c0fSAndriy Voskoboinyk if (data->ni != NULL && data->ni->ni_vap == vap) { 426519e6c0fSAndriy Voskoboinyk /* 427519e6c0fSAndriy Voskoboinyk * NB: if some vap is still running 428519e6c0fSAndriy Voskoboinyk * rtwn_pci_tx_done() will free the mbuf; 429519e6c0fSAndriy Voskoboinyk * otherwise, rtwn_stop() will reset all rings 430519e6c0fSAndriy Voskoboinyk * after device shutdown. 431519e6c0fSAndriy Voskoboinyk */ 432519e6c0fSAndriy Voskoboinyk ieee80211_free_node(data->ni); 433519e6c0fSAndriy Voskoboinyk data->ni = NULL; 434519e6c0fSAndriy Voskoboinyk } 435519e6c0fSAndriy Voskoboinyk } 436519e6c0fSAndriy Voskoboinyk } 4377453645fSAndriy Voskoboinyk } 4387453645fSAndriy Voskoboinyk 4397453645fSAndriy Voskoboinyk static void 4407453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(struct rtwn_softc *sc, int qid) 4417453645fSAndriy Voskoboinyk { 4427453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 4437453645fSAndriy Voskoboinyk struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; 4447453645fSAndriy Voskoboinyk struct rtwn_tx_data *tx_data; 4457453645fSAndriy Voskoboinyk int i; 4467453645fSAndriy Voskoboinyk 4477453645fSAndriy Voskoboinyk if (tx_ring->desc_dmat != NULL) { 4487453645fSAndriy Voskoboinyk if (tx_ring->desc != NULL) { 4497453645fSAndriy Voskoboinyk bus_dmamap_sync(tx_ring->desc_dmat, 4507453645fSAndriy Voskoboinyk tx_ring->desc_map, BUS_DMASYNC_POSTWRITE); 4517453645fSAndriy Voskoboinyk bus_dmamap_unload(tx_ring->desc_dmat, 4527453645fSAndriy Voskoboinyk tx_ring->desc_map); 4537453645fSAndriy Voskoboinyk bus_dmamem_free(tx_ring->desc_dmat, tx_ring->desc, 4547453645fSAndriy Voskoboinyk tx_ring->desc_map); 4557453645fSAndriy Voskoboinyk } 4567453645fSAndriy Voskoboinyk bus_dma_tag_destroy(tx_ring->desc_dmat); 4577453645fSAndriy Voskoboinyk } 4587453645fSAndriy Voskoboinyk 4597453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 4607453645fSAndriy Voskoboinyk tx_data = &tx_ring->tx_data[i]; 4617453645fSAndriy Voskoboinyk 4627453645fSAndriy Voskoboinyk if (tx_data->m != NULL) { 4637453645fSAndriy Voskoboinyk bus_dmamap_sync(tx_ring->data_dmat, tx_data->map, 4647453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 4657453645fSAndriy Voskoboinyk bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); 4667453645fSAndriy Voskoboinyk m_freem(tx_data->m); 4677453645fSAndriy Voskoboinyk tx_data->m = NULL; 4687453645fSAndriy Voskoboinyk } 4697453645fSAndriy Voskoboinyk } 4707453645fSAndriy Voskoboinyk if (tx_ring->data_dmat != NULL) { 4717453645fSAndriy Voskoboinyk bus_dma_tag_destroy(tx_ring->data_dmat); 4727453645fSAndriy Voskoboinyk tx_ring->data_dmat = NULL; 4737453645fSAndriy Voskoboinyk } 4747453645fSAndriy Voskoboinyk 4757453645fSAndriy Voskoboinyk sc->qfullmsk &= ~(1 << qid); 4767453645fSAndriy Voskoboinyk tx_ring->queued = 0; 4777453645fSAndriy Voskoboinyk tx_ring->last = tx_ring->cur = 0; 4787453645fSAndriy Voskoboinyk } 4797453645fSAndriy Voskoboinyk 4807453645fSAndriy Voskoboinyk static void 4817453645fSAndriy Voskoboinyk rtwn_pci_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap) 4827453645fSAndriy Voskoboinyk { 4837453645fSAndriy Voskoboinyk int i; 4847453645fSAndriy Voskoboinyk 4857453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) 4867453645fSAndriy Voskoboinyk rtwn_pci_reset_tx_list(sc, vap, i); 4877453645fSAndriy Voskoboinyk 4887453645fSAndriy Voskoboinyk if (vap == NULL) { 4897453645fSAndriy Voskoboinyk sc->qfullmsk = 0; 4907453645fSAndriy Voskoboinyk rtwn_pci_reset_rx_list(sc); 4917453645fSAndriy Voskoboinyk } 4927453645fSAndriy Voskoboinyk } 4937453645fSAndriy Voskoboinyk 4947453645fSAndriy Voskoboinyk static int 4957453645fSAndriy Voskoboinyk rtwn_pci_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf, 4967453645fSAndriy Voskoboinyk uint16_t reg, int mlen) 4977453645fSAndriy Voskoboinyk { 4987453645fSAndriy Voskoboinyk int i; 4997453645fSAndriy Voskoboinyk 5007453645fSAndriy Voskoboinyk for (i = 0; i < mlen; i++) 5017453645fSAndriy Voskoboinyk rtwn_pci_write_1(sc, reg++, buf[i]); 5027453645fSAndriy Voskoboinyk 5037453645fSAndriy Voskoboinyk /* NB: cannot fail */ 5047453645fSAndriy Voskoboinyk return (0); 5057453645fSAndriy Voskoboinyk } 5067453645fSAndriy Voskoboinyk 5077453645fSAndriy Voskoboinyk static uint16_t 5087453645fSAndriy Voskoboinyk rtwn_pci_get_qmap(struct rtwn_softc *sc) 5097453645fSAndriy Voskoboinyk { 5107453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 5117453645fSAndriy Voskoboinyk 5127453645fSAndriy Voskoboinyk KASSERT(pc->pc_qmap != 0, ("%s: qmap is not set!\n", __func__)); 5137453645fSAndriy Voskoboinyk 5147453645fSAndriy Voskoboinyk return (pc->pc_qmap); 5157453645fSAndriy Voskoboinyk } 5167453645fSAndriy Voskoboinyk 5177453645fSAndriy Voskoboinyk static void 5187453645fSAndriy Voskoboinyk rtwn_pci_set_desc_addr(struct rtwn_softc *sc) 5197453645fSAndriy Voskoboinyk { 5207453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 5217453645fSAndriy Voskoboinyk 5227453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RESET, "%s: addresses:\n" 5237453645fSAndriy Voskoboinyk "bk: %08jX, be: %08jX, vi: %08jX, vo: %08jX\n" 5247453645fSAndriy Voskoboinyk "bcn: %08jX, mgt: %08jX, high: %08jX, rx: %08jX\n", 5257453645fSAndriy Voskoboinyk __func__, (uintmax_t)pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr, 5267453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr, 5277453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr, 5287453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr, 5297453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr, 5307453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr, 5317453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr, 5327453645fSAndriy Voskoboinyk (uintmax_t)pc->rx_ring.paddr); 5337453645fSAndriy Voskoboinyk 5347453645fSAndriy Voskoboinyk /* Set Tx Configuration Register. */ 5357453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_TCR, pc->tcr); 5367453645fSAndriy Voskoboinyk 5377453645fSAndriy Voskoboinyk /* Configure Tx DMA. */ 5387453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_BKQ_DESA, 5397453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr); 5407453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_BEQ_DESA, 5417453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr); 5427453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_VIQ_DESA, 5437453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr); 5447453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_VOQ_DESA, 5457453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr); 5467453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_BCNQ_DESA, 5477453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr); 5487453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_MGQ_DESA, 5497453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr); 5507453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_HQ_DESA, 5517453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr); 5527453645fSAndriy Voskoboinyk 5537453645fSAndriy Voskoboinyk /* Configure Rx DMA. */ 5547453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_RX_DESA, pc->rx_ring.paddr); 5557453645fSAndriy Voskoboinyk } 5567453645fSAndriy Voskoboinyk 5577453645fSAndriy Voskoboinyk static void 558d067ef0fSAndriy Voskoboinyk rtwn_pci_beacon_update_begin(struct rtwn_softc *sc, struct ieee80211vap *vap) 559d067ef0fSAndriy Voskoboinyk { 560d067ef0fSAndriy Voskoboinyk struct rtwn_vap *rvp = RTWN_VAP(vap); 561d067ef0fSAndriy Voskoboinyk 562d067ef0fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 563d067ef0fSAndriy Voskoboinyk 564d067ef0fSAndriy Voskoboinyk rtwn_beacon_enable(sc, rvp->id, 0); 565d067ef0fSAndriy Voskoboinyk } 566d067ef0fSAndriy Voskoboinyk 567d067ef0fSAndriy Voskoboinyk static void 568d067ef0fSAndriy Voskoboinyk rtwn_pci_beacon_update_end(struct rtwn_softc *sc, struct ieee80211vap *vap) 569d067ef0fSAndriy Voskoboinyk { 570d067ef0fSAndriy Voskoboinyk struct rtwn_vap *rvp = RTWN_VAP(vap); 571d067ef0fSAndriy Voskoboinyk 572d067ef0fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 573d067ef0fSAndriy Voskoboinyk 574d067ef0fSAndriy Voskoboinyk if (rvp->curr_mode != R92C_MSR_NOLINK) 575d067ef0fSAndriy Voskoboinyk rtwn_beacon_enable(sc, rvp->id, 1); 576d067ef0fSAndriy Voskoboinyk } 577d067ef0fSAndriy Voskoboinyk 578d067ef0fSAndriy Voskoboinyk static void 5797453645fSAndriy Voskoboinyk rtwn_pci_attach_methods(struct rtwn_softc *sc) 5807453645fSAndriy Voskoboinyk { 5817453645fSAndriy Voskoboinyk sc->sc_write_1 = rtwn_pci_write_1; 5827453645fSAndriy Voskoboinyk sc->sc_write_2 = rtwn_pci_write_2; 5837453645fSAndriy Voskoboinyk sc->sc_write_4 = rtwn_pci_write_4; 5847453645fSAndriy Voskoboinyk sc->sc_read_1 = rtwn_pci_read_1; 5857453645fSAndriy Voskoboinyk sc->sc_read_2 = rtwn_pci_read_2; 5867453645fSAndriy Voskoboinyk sc->sc_read_4 = rtwn_pci_read_4; 5877453645fSAndriy Voskoboinyk sc->sc_delay = rtwn_pci_delay; 5887453645fSAndriy Voskoboinyk sc->sc_tx_start = rtwn_pci_tx_start; 5897453645fSAndriy Voskoboinyk sc->sc_reset_lists = rtwn_pci_reset_lists; 5907453645fSAndriy Voskoboinyk sc->sc_abort_xfers = rtwn_nop_softc; 5917453645fSAndriy Voskoboinyk sc->sc_fw_write_block = rtwn_pci_fw_write_block; 5927453645fSAndriy Voskoboinyk sc->sc_get_qmap = rtwn_pci_get_qmap; 5937453645fSAndriy Voskoboinyk sc->sc_set_desc_addr = rtwn_pci_set_desc_addr; 5947453645fSAndriy Voskoboinyk sc->sc_drop_incorrect_tx = rtwn_nop_softc; 595d067ef0fSAndriy Voskoboinyk sc->sc_beacon_update_begin = rtwn_pci_beacon_update_begin; 596d067ef0fSAndriy Voskoboinyk sc->sc_beacon_update_end = rtwn_pci_beacon_update_end; 597d067ef0fSAndriy Voskoboinyk sc->sc_beacon_unload = rtwn_pci_reset_beacon_ring; 598d067ef0fSAndriy Voskoboinyk 599d067ef0fSAndriy Voskoboinyk sc->bcn_check_interval = 25000; 6007453645fSAndriy Voskoboinyk } 6017453645fSAndriy Voskoboinyk 6027453645fSAndriy Voskoboinyk static int 6037453645fSAndriy Voskoboinyk rtwn_pci_attach(device_t dev) 6047453645fSAndriy Voskoboinyk { 605*ae60d856SAndriy Voskoboinyk const struct rtwn_pci_ident *ident; 6067453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(dev); 6077453645fSAndriy Voskoboinyk struct rtwn_softc *sc = &pc->pc_sc; 6087453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 6097453645fSAndriy Voskoboinyk uint32_t lcsr; 6107453645fSAndriy Voskoboinyk int cap_off, i, error, rid; 6117453645fSAndriy Voskoboinyk 612*ae60d856SAndriy Voskoboinyk ident = rtwn_pci_probe_sub(dev); 613*ae60d856SAndriy Voskoboinyk if (ident == NULL) 6147453645fSAndriy Voskoboinyk return (ENXIO); 6157453645fSAndriy Voskoboinyk 6167453645fSAndriy Voskoboinyk /* 6177453645fSAndriy Voskoboinyk * Get the offset of the PCI Express Capability Structure in PCI 6187453645fSAndriy Voskoboinyk * Configuration Space. 6197453645fSAndriy Voskoboinyk */ 6207453645fSAndriy Voskoboinyk error = pci_find_cap(dev, PCIY_EXPRESS, &cap_off); 6217453645fSAndriy Voskoboinyk if (error != 0) { 6227453645fSAndriy Voskoboinyk device_printf(dev, "PCIe capability structure not found!\n"); 6237453645fSAndriy Voskoboinyk return (error); 6247453645fSAndriy Voskoboinyk } 6257453645fSAndriy Voskoboinyk 6267453645fSAndriy Voskoboinyk /* Enable bus-mastering. */ 6277453645fSAndriy Voskoboinyk pci_enable_busmaster(dev); 6287453645fSAndriy Voskoboinyk 6297453645fSAndriy Voskoboinyk rid = PCIR_BAR(2); 6307453645fSAndriy Voskoboinyk pc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 6317453645fSAndriy Voskoboinyk RF_ACTIVE); 6327453645fSAndriy Voskoboinyk if (pc->mem == NULL) { 6337453645fSAndriy Voskoboinyk device_printf(dev, "can't map mem space\n"); 6347453645fSAndriy Voskoboinyk return (ENOMEM); 6357453645fSAndriy Voskoboinyk } 6367453645fSAndriy Voskoboinyk pc->pc_st = rman_get_bustag(pc->mem); 6377453645fSAndriy Voskoboinyk pc->pc_sh = rman_get_bushandle(pc->mem); 6387453645fSAndriy Voskoboinyk 6397453645fSAndriy Voskoboinyk /* Install interrupt handler. */ 6407453645fSAndriy Voskoboinyk rid = 1; 6417453645fSAndriy Voskoboinyk if (pci_alloc_msi(dev, &rid) == 0) 6427453645fSAndriy Voskoboinyk rid = 1; 6437453645fSAndriy Voskoboinyk else 6447453645fSAndriy Voskoboinyk rid = 0; 6457453645fSAndriy Voskoboinyk pc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | 6467453645fSAndriy Voskoboinyk (rid != 0 ? 0 : RF_SHAREABLE)); 6477453645fSAndriy Voskoboinyk if (pc->irq == NULL) { 6487453645fSAndriy Voskoboinyk device_printf(dev, "can't map interrupt\n"); 6497453645fSAndriy Voskoboinyk goto detach; 6507453645fSAndriy Voskoboinyk } 6517453645fSAndriy Voskoboinyk 6527453645fSAndriy Voskoboinyk /* Disable PCIe Active State Power Management (ASPM). */ 6537453645fSAndriy Voskoboinyk lcsr = pci_read_config(dev, cap_off + PCIER_LINK_CTL, 4); 6547453645fSAndriy Voskoboinyk lcsr &= ~PCIEM_LINK_CTL_ASPMC; 6557453645fSAndriy Voskoboinyk pci_write_config(dev, cap_off + PCIER_LINK_CTL, lcsr, 4); 6567453645fSAndriy Voskoboinyk 6577453645fSAndriy Voskoboinyk sc->sc_dev = dev; 6587453645fSAndriy Voskoboinyk ic->ic_name = device_get_nameunit(dev); 6597453645fSAndriy Voskoboinyk 6607453645fSAndriy Voskoboinyk /* Need to be initialized early. */ 6617453645fSAndriy Voskoboinyk rtwn_sysctlattach(sc); 6627453645fSAndriy Voskoboinyk mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF); 6637453645fSAndriy Voskoboinyk 6647453645fSAndriy Voskoboinyk rtwn_pci_attach_methods(sc); 665*ae60d856SAndriy Voskoboinyk rtwn_pci_attach_private(pc, ident->chip); 6667453645fSAndriy Voskoboinyk 6677453645fSAndriy Voskoboinyk /* Allocate Tx/Rx buffers. */ 6687453645fSAndriy Voskoboinyk error = rtwn_pci_alloc_rx_list(sc); 6697453645fSAndriy Voskoboinyk if (error != 0) { 6707453645fSAndriy Voskoboinyk device_printf(dev, 6717453645fSAndriy Voskoboinyk "could not allocate Rx buffers, error %d\n", 6727453645fSAndriy Voskoboinyk error); 6737453645fSAndriy Voskoboinyk goto detach; 6747453645fSAndriy Voskoboinyk } 6757453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) { 6767453645fSAndriy Voskoboinyk error = rtwn_pci_alloc_tx_list(sc, i); 6777453645fSAndriy Voskoboinyk if (error != 0) { 6787453645fSAndriy Voskoboinyk device_printf(dev, 6797453645fSAndriy Voskoboinyk "could not allocate Tx buffers, error %d\n", 6807453645fSAndriy Voskoboinyk error); 6817453645fSAndriy Voskoboinyk goto detach; 6827453645fSAndriy Voskoboinyk } 6837453645fSAndriy Voskoboinyk } 6847453645fSAndriy Voskoboinyk 6857453645fSAndriy Voskoboinyk /* Generic attach. */ 6867453645fSAndriy Voskoboinyk error = rtwn_attach(sc); 6877453645fSAndriy Voskoboinyk if (error != 0) 6887453645fSAndriy Voskoboinyk goto detach; 6897453645fSAndriy Voskoboinyk 6907453645fSAndriy Voskoboinyk /* 6917453645fSAndriy Voskoboinyk * Hook our interrupt after all initialization is complete. 6927453645fSAndriy Voskoboinyk */ 6937453645fSAndriy Voskoboinyk error = bus_setup_intr(dev, pc->irq, INTR_TYPE_NET | INTR_MPSAFE, 6947453645fSAndriy Voskoboinyk NULL, rtwn_pci_intr, sc, &pc->pc_ih); 6957453645fSAndriy Voskoboinyk if (error != 0) { 6967453645fSAndriy Voskoboinyk device_printf(dev, "can't establish interrupt, error %d\n", 6977453645fSAndriy Voskoboinyk error); 6987453645fSAndriy Voskoboinyk goto detach; 6997453645fSAndriy Voskoboinyk } 7007453645fSAndriy Voskoboinyk 7017453645fSAndriy Voskoboinyk return (0); 7027453645fSAndriy Voskoboinyk 7037453645fSAndriy Voskoboinyk detach: 7047453645fSAndriy Voskoboinyk rtwn_pci_detach(dev); /* failure */ 7057453645fSAndriy Voskoboinyk return (ENXIO); 7067453645fSAndriy Voskoboinyk } 7077453645fSAndriy Voskoboinyk 7087453645fSAndriy Voskoboinyk static int 7097453645fSAndriy Voskoboinyk rtwn_pci_detach(device_t dev) 7107453645fSAndriy Voskoboinyk { 7117453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(dev); 7127453645fSAndriy Voskoboinyk struct rtwn_softc *sc = &pc->pc_sc; 7137453645fSAndriy Voskoboinyk int i; 7147453645fSAndriy Voskoboinyk 7157453645fSAndriy Voskoboinyk /* Generic detach. */ 7167453645fSAndriy Voskoboinyk rtwn_detach(sc); 7177453645fSAndriy Voskoboinyk 7187453645fSAndriy Voskoboinyk /* Uninstall interrupt handler. */ 7197453645fSAndriy Voskoboinyk if (pc->irq != NULL) { 7207453645fSAndriy Voskoboinyk bus_teardown_intr(dev, pc->irq, pc->pc_ih); 7217453645fSAndriy Voskoboinyk bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(pc->irq), 7227453645fSAndriy Voskoboinyk pc->irq); 7237453645fSAndriy Voskoboinyk pci_release_msi(dev); 7247453645fSAndriy Voskoboinyk } 7257453645fSAndriy Voskoboinyk 7267453645fSAndriy Voskoboinyk /* Free Tx/Rx buffers. */ 7277453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) 7287453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(sc, i); 7297453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(sc); 7307453645fSAndriy Voskoboinyk 7317453645fSAndriy Voskoboinyk if (pc->mem != NULL) 7327453645fSAndriy Voskoboinyk bus_release_resource(dev, SYS_RES_MEMORY, 7337453645fSAndriy Voskoboinyk rman_get_rid(pc->mem), pc->mem); 7347453645fSAndriy Voskoboinyk 7357453645fSAndriy Voskoboinyk rtwn_detach_private(sc); 7367453645fSAndriy Voskoboinyk mtx_destroy(&sc->sc_mtx); 7377453645fSAndriy Voskoboinyk 7387453645fSAndriy Voskoboinyk return (0); 7397453645fSAndriy Voskoboinyk } 7407453645fSAndriy Voskoboinyk 7417453645fSAndriy Voskoboinyk static int 7427453645fSAndriy Voskoboinyk rtwn_pci_shutdown(device_t self) 7437453645fSAndriy Voskoboinyk { 7447453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(self); 7457453645fSAndriy Voskoboinyk 7467453645fSAndriy Voskoboinyk ieee80211_stop_all(&pc->pc_sc.sc_ic); 7477453645fSAndriy Voskoboinyk return (0); 7487453645fSAndriy Voskoboinyk } 7497453645fSAndriy Voskoboinyk 7507453645fSAndriy Voskoboinyk static int 7517453645fSAndriy Voskoboinyk rtwn_pci_suspend(device_t self) 7527453645fSAndriy Voskoboinyk { 7537453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(self); 7547453645fSAndriy Voskoboinyk 7557453645fSAndriy Voskoboinyk rtwn_suspend(&pc->pc_sc); 7567453645fSAndriy Voskoboinyk 7577453645fSAndriy Voskoboinyk return (0); 7587453645fSAndriy Voskoboinyk } 7597453645fSAndriy Voskoboinyk 7607453645fSAndriy Voskoboinyk static int 7617453645fSAndriy Voskoboinyk rtwn_pci_resume(device_t self) 7627453645fSAndriy Voskoboinyk { 7637453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(self); 7647453645fSAndriy Voskoboinyk 7657453645fSAndriy Voskoboinyk rtwn_resume(&pc->pc_sc); 7667453645fSAndriy Voskoboinyk 7677453645fSAndriy Voskoboinyk return (0); 7687453645fSAndriy Voskoboinyk } 7697453645fSAndriy Voskoboinyk 7707453645fSAndriy Voskoboinyk static device_method_t rtwn_pci_methods[] = { 7717453645fSAndriy Voskoboinyk /* Device interface */ 7727453645fSAndriy Voskoboinyk DEVMETHOD(device_probe, rtwn_pci_probe), 7737453645fSAndriy Voskoboinyk DEVMETHOD(device_attach, rtwn_pci_attach), 7747453645fSAndriy Voskoboinyk DEVMETHOD(device_detach, rtwn_pci_detach), 7757453645fSAndriy Voskoboinyk DEVMETHOD(device_shutdown, rtwn_pci_shutdown), 7767453645fSAndriy Voskoboinyk DEVMETHOD(device_suspend, rtwn_pci_suspend), 7777453645fSAndriy Voskoboinyk DEVMETHOD(device_resume, rtwn_pci_resume), 7787453645fSAndriy Voskoboinyk 7797453645fSAndriy Voskoboinyk DEVMETHOD_END 7807453645fSAndriy Voskoboinyk }; 7817453645fSAndriy Voskoboinyk 7827453645fSAndriy Voskoboinyk static driver_t rtwn_pci_driver = { 7837453645fSAndriy Voskoboinyk "rtwn", 7847453645fSAndriy Voskoboinyk rtwn_pci_methods, 7857453645fSAndriy Voskoboinyk sizeof(struct rtwn_pci_softc) 7867453645fSAndriy Voskoboinyk }; 7877453645fSAndriy Voskoboinyk 7887453645fSAndriy Voskoboinyk static devclass_t rtwn_pci_devclass; 7897453645fSAndriy Voskoboinyk 7907453645fSAndriy Voskoboinyk DRIVER_MODULE(rtwn_pci, pci, rtwn_pci_driver, rtwn_pci_devclass, NULL, NULL); 7917453645fSAndriy Voskoboinyk MODULE_VERSION(rtwn_pci, 1); 7927453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, pci, 1, 1, 1); 7937453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, wlan, 1, 1, 1); 7947453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, rtwn, 2, 2, 2); 795