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 977453645fSAndriy Voskoboinyk static int matched_chip = RTWN_CHIP_MAX_PCI; 987453645fSAndriy Voskoboinyk 997453645fSAndriy Voskoboinyk static int 1007453645fSAndriy Voskoboinyk rtwn_pci_probe(device_t dev) 1017453645fSAndriy Voskoboinyk { 1027453645fSAndriy Voskoboinyk const struct rtwn_pci_ident *ident; 1037453645fSAndriy Voskoboinyk 1047453645fSAndriy Voskoboinyk for (ident = rtwn_pci_ident_table; ident->name != NULL; ident++) { 1057453645fSAndriy Voskoboinyk if (pci_get_vendor(dev) == ident->vendor && 1067453645fSAndriy Voskoboinyk pci_get_device(dev) == ident->device) { 1077453645fSAndriy Voskoboinyk matched_chip = ident->chip; 1087453645fSAndriy Voskoboinyk device_set_desc(dev, ident->name); 1097453645fSAndriy Voskoboinyk return (BUS_PROBE_DEFAULT); 1107453645fSAndriy Voskoboinyk } 1117453645fSAndriy Voskoboinyk } 1127453645fSAndriy Voskoboinyk return (ENXIO); 1137453645fSAndriy Voskoboinyk } 1147453645fSAndriy Voskoboinyk 1157453645fSAndriy Voskoboinyk static int 1167453645fSAndriy Voskoboinyk rtwn_pci_alloc_rx_list(struct rtwn_softc *sc) 1177453645fSAndriy Voskoboinyk { 1187453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 1197453645fSAndriy Voskoboinyk struct rtwn_rx_ring *rx_ring = &pc->rx_ring; 1207453645fSAndriy Voskoboinyk struct rtwn_rx_data *rx_data; 1217453645fSAndriy Voskoboinyk bus_size_t size; 1227453645fSAndriy Voskoboinyk int i, error; 1237453645fSAndriy Voskoboinyk 1247453645fSAndriy Voskoboinyk /* Allocate Rx descriptors. */ 1257453645fSAndriy Voskoboinyk size = sizeof(struct r92ce_rx_stat) * RTWN_PCI_RX_LIST_COUNT; 1267453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1277453645fSAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 1287453645fSAndriy Voskoboinyk size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat); 1297453645fSAndriy Voskoboinyk if (error != 0) { 1307453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create rx desc DMA tag\n"); 1317453645fSAndriy Voskoboinyk goto fail; 1327453645fSAndriy Voskoboinyk } 1337453645fSAndriy Voskoboinyk 1347453645fSAndriy Voskoboinyk error = bus_dmamem_alloc(rx_ring->desc_dmat, (void **)&rx_ring->desc, 1357453645fSAndriy Voskoboinyk BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1367453645fSAndriy Voskoboinyk &rx_ring->desc_map); 1377453645fSAndriy Voskoboinyk if (error != 0) { 1387453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not allocate rx desc\n"); 1397453645fSAndriy Voskoboinyk goto fail; 1407453645fSAndriy Voskoboinyk } 1417453645fSAndriy Voskoboinyk error = bus_dmamap_load(rx_ring->desc_dmat, rx_ring->desc_map, 1427453645fSAndriy Voskoboinyk rx_ring->desc, size, rtwn_pci_dma_map_addr, &rx_ring->paddr, 0); 1437453645fSAndriy Voskoboinyk if (error != 0) { 1447453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not load rx desc DMA map\n"); 1457453645fSAndriy Voskoboinyk goto fail; 1467453645fSAndriy Voskoboinyk } 1477453645fSAndriy Voskoboinyk bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map, 1487453645fSAndriy Voskoboinyk BUS_DMASYNC_PREWRITE); 1497453645fSAndriy Voskoboinyk 1507453645fSAndriy Voskoboinyk /* Create RX buffer DMA tag. */ 1517453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 152*a14f9888SAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 153*a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &rx_ring->data_dmat); 1547453645fSAndriy Voskoboinyk if (error != 0) { 1557453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); 1567453645fSAndriy Voskoboinyk goto fail; 1577453645fSAndriy Voskoboinyk } 1587453645fSAndriy Voskoboinyk 1597453645fSAndriy Voskoboinyk /* Allocate Rx buffers. */ 1607453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { 1617453645fSAndriy Voskoboinyk rx_data = &rx_ring->rx_data[i]; 1627453645fSAndriy Voskoboinyk error = bus_dmamap_create(rx_ring->data_dmat, 0, &rx_data->map); 1637453645fSAndriy Voskoboinyk if (error != 0) { 1647453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1657453645fSAndriy Voskoboinyk "could not create rx buf DMA map\n"); 1667453645fSAndriy Voskoboinyk goto fail; 1677453645fSAndriy Voskoboinyk } 1687453645fSAndriy Voskoboinyk 169*a14f9888SAndriy Voskoboinyk rx_data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, 170*a14f9888SAndriy Voskoboinyk MJUMPAGESIZE); 1717453645fSAndriy Voskoboinyk if (rx_data->m == NULL) { 1727453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1737453645fSAndriy Voskoboinyk "could not allocate rx mbuf\n"); 1747453645fSAndriy Voskoboinyk error = ENOMEM; 1757453645fSAndriy Voskoboinyk goto fail; 1767453645fSAndriy Voskoboinyk } 1777453645fSAndriy Voskoboinyk 1787453645fSAndriy Voskoboinyk error = bus_dmamap_load(rx_ring->data_dmat, rx_data->map, 179*a14f9888SAndriy Voskoboinyk mtod(rx_data->m, void *), MJUMPAGESIZE, 180*a14f9888SAndriy Voskoboinyk rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT); 1817453645fSAndriy Voskoboinyk if (error != 0) { 1827453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1837453645fSAndriy Voskoboinyk "could not load rx buf DMA map"); 1847453645fSAndriy Voskoboinyk goto fail; 1857453645fSAndriy Voskoboinyk } 1867453645fSAndriy Voskoboinyk 1877453645fSAndriy Voskoboinyk rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], rx_data->paddr, 188*a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, i); 1897453645fSAndriy Voskoboinyk } 1907453645fSAndriy Voskoboinyk rx_ring->cur = 0; 1917453645fSAndriy Voskoboinyk 1927453645fSAndriy Voskoboinyk return (0); 1937453645fSAndriy Voskoboinyk 1947453645fSAndriy Voskoboinyk fail: 1957453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(sc); 1967453645fSAndriy Voskoboinyk return (error); 1977453645fSAndriy Voskoboinyk } 1987453645fSAndriy Voskoboinyk 1997453645fSAndriy Voskoboinyk static void 2007453645fSAndriy Voskoboinyk rtwn_pci_reset_rx_list(struct rtwn_softc *sc) 2017453645fSAndriy Voskoboinyk { 2027453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 2037453645fSAndriy Voskoboinyk struct rtwn_rx_ring *rx_ring = &pc->rx_ring; 2047453645fSAndriy Voskoboinyk struct rtwn_rx_data *rx_data; 2057453645fSAndriy Voskoboinyk int i; 2067453645fSAndriy Voskoboinyk 2077453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { 2087453645fSAndriy Voskoboinyk rx_data = &rx_ring->rx_data[i]; 2097453645fSAndriy Voskoboinyk rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], 210*a14f9888SAndriy Voskoboinyk rx_data->paddr, MJUMPAGESIZE, i); 2117453645fSAndriy Voskoboinyk } 2127453645fSAndriy Voskoboinyk rx_ring->cur = 0; 2137453645fSAndriy Voskoboinyk } 2147453645fSAndriy Voskoboinyk 2157453645fSAndriy Voskoboinyk static void 2167453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(struct rtwn_softc *sc) 2177453645fSAndriy Voskoboinyk { 2187453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 2197453645fSAndriy Voskoboinyk struct rtwn_rx_ring *rx_ring = &pc->rx_ring; 2207453645fSAndriy Voskoboinyk struct rtwn_rx_data *rx_data; 2217453645fSAndriy Voskoboinyk int i; 2227453645fSAndriy Voskoboinyk 2237453645fSAndriy Voskoboinyk if (rx_ring->desc_dmat != NULL) { 2247453645fSAndriy Voskoboinyk if (rx_ring->desc != NULL) { 2257453645fSAndriy Voskoboinyk bus_dmamap_sync(rx_ring->desc_dmat, 2267453645fSAndriy Voskoboinyk rx_ring->desc_map, 2277453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2287453645fSAndriy Voskoboinyk bus_dmamap_unload(rx_ring->desc_dmat, 2297453645fSAndriy Voskoboinyk rx_ring->desc_map); 2307453645fSAndriy Voskoboinyk bus_dmamem_free(rx_ring->desc_dmat, rx_ring->desc, 2317453645fSAndriy Voskoboinyk rx_ring->desc_map); 2327453645fSAndriy Voskoboinyk rx_ring->desc = NULL; 2337453645fSAndriy Voskoboinyk } 2347453645fSAndriy Voskoboinyk bus_dma_tag_destroy(rx_ring->desc_dmat); 2357453645fSAndriy Voskoboinyk rx_ring->desc_dmat = NULL; 2367453645fSAndriy Voskoboinyk } 2377453645fSAndriy Voskoboinyk 2387453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) { 2397453645fSAndriy Voskoboinyk rx_data = &rx_ring->rx_data[i]; 2407453645fSAndriy Voskoboinyk 2417453645fSAndriy Voskoboinyk if (rx_data->m != NULL) { 2427453645fSAndriy Voskoboinyk bus_dmamap_sync(rx_ring->data_dmat, 2437453645fSAndriy Voskoboinyk rx_data->map, BUS_DMASYNC_POSTREAD); 2447453645fSAndriy Voskoboinyk bus_dmamap_unload(rx_ring->data_dmat, rx_data->map); 2457453645fSAndriy Voskoboinyk m_freem(rx_data->m); 2467453645fSAndriy Voskoboinyk rx_data->m = NULL; 2477453645fSAndriy Voskoboinyk } 2487453645fSAndriy Voskoboinyk bus_dmamap_destroy(rx_ring->data_dmat, rx_data->map); 2497453645fSAndriy Voskoboinyk rx_data->map = NULL; 2507453645fSAndriy Voskoboinyk } 2517453645fSAndriy Voskoboinyk if (rx_ring->data_dmat != NULL) { 2527453645fSAndriy Voskoboinyk bus_dma_tag_destroy(rx_ring->data_dmat); 2537453645fSAndriy Voskoboinyk rx_ring->data_dmat = NULL; 2547453645fSAndriy Voskoboinyk } 2557453645fSAndriy Voskoboinyk } 2567453645fSAndriy Voskoboinyk 2577453645fSAndriy Voskoboinyk static int 2587453645fSAndriy Voskoboinyk rtwn_pci_alloc_tx_list(struct rtwn_softc *sc, int qid) 2597453645fSAndriy Voskoboinyk { 2607453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 2617453645fSAndriy Voskoboinyk struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; 2627453645fSAndriy Voskoboinyk bus_size_t size; 2637453645fSAndriy Voskoboinyk int i, error; 2647453645fSAndriy Voskoboinyk 2657453645fSAndriy Voskoboinyk size = sc->txdesc_len * RTWN_PCI_TX_LIST_COUNT; 2667453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), PAGE_SIZE, 0, 2677453645fSAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2687453645fSAndriy Voskoboinyk size, 1, size, 0, NULL, NULL, &tx_ring->desc_dmat); 2697453645fSAndriy Voskoboinyk if (error != 0) { 2707453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); 2717453645fSAndriy Voskoboinyk goto fail; 2727453645fSAndriy Voskoboinyk } 2737453645fSAndriy Voskoboinyk 2747453645fSAndriy Voskoboinyk error = bus_dmamem_alloc(tx_ring->desc_dmat, &tx_ring->desc, 2757453645fSAndriy Voskoboinyk BUS_DMA_NOWAIT | BUS_DMA_ZERO, &tx_ring->desc_map); 2767453645fSAndriy Voskoboinyk if (error != 0) { 2777453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "can't map tx ring DMA memory\n"); 2787453645fSAndriy Voskoboinyk goto fail; 2797453645fSAndriy Voskoboinyk } 2807453645fSAndriy Voskoboinyk error = bus_dmamap_load(tx_ring->desc_dmat, tx_ring->desc_map, 2817453645fSAndriy Voskoboinyk tx_ring->desc, size, rtwn_pci_dma_map_addr, &tx_ring->paddr, 2827453645fSAndriy Voskoboinyk BUS_DMA_NOWAIT); 2837453645fSAndriy Voskoboinyk if (error != 0) { 2847453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not load desc DMA map\n"); 2857453645fSAndriy Voskoboinyk goto fail; 2867453645fSAndriy Voskoboinyk } 2877453645fSAndriy Voskoboinyk bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, 2887453645fSAndriy Voskoboinyk BUS_DMASYNC_PREWRITE); 2897453645fSAndriy Voskoboinyk 2907453645fSAndriy Voskoboinyk error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 291*a14f9888SAndriy Voskoboinyk BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 292*a14f9888SAndriy Voskoboinyk MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &tx_ring->data_dmat); 2937453645fSAndriy Voskoboinyk if (error != 0) { 2947453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); 2957453645fSAndriy Voskoboinyk goto fail; 2967453645fSAndriy Voskoboinyk } 2977453645fSAndriy Voskoboinyk 2987453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 2997453645fSAndriy Voskoboinyk struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; 3007453645fSAndriy Voskoboinyk void *tx_desc = (uint8_t *)tx_ring->desc + sc->txdesc_len * i; 3017453645fSAndriy Voskoboinyk uint32_t next_desc_addr = tx_ring->paddr + 3027453645fSAndriy Voskoboinyk sc->txdesc_len * ((i + 1) % RTWN_PCI_TX_LIST_COUNT); 3037453645fSAndriy Voskoboinyk 3047453645fSAndriy Voskoboinyk rtwn_pci_setup_tx_desc(pc, tx_desc, next_desc_addr); 3057453645fSAndriy Voskoboinyk 3067453645fSAndriy Voskoboinyk error = bus_dmamap_create(tx_ring->data_dmat, 0, &tx_data->map); 3077453645fSAndriy Voskoboinyk if (error != 0) { 3087453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 3097453645fSAndriy Voskoboinyk "could not create tx buf DMA map\n"); 3107453645fSAndriy Voskoboinyk return (error); 3117453645fSAndriy Voskoboinyk } 3127453645fSAndriy Voskoboinyk tx_data->m = NULL; 3137453645fSAndriy Voskoboinyk tx_data->ni = NULL; 3147453645fSAndriy Voskoboinyk } 3157453645fSAndriy Voskoboinyk return (0); 3167453645fSAndriy Voskoboinyk 3177453645fSAndriy Voskoboinyk fail: 3187453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(sc, qid); 3197453645fSAndriy Voskoboinyk return (error); 3207453645fSAndriy Voskoboinyk } 3217453645fSAndriy Voskoboinyk 3227453645fSAndriy Voskoboinyk static void 323519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *sc, int qid) 3247453645fSAndriy Voskoboinyk { 3257453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 326519e6c0fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; 327519e6c0fSAndriy Voskoboinyk int i; 3287453645fSAndriy Voskoboinyk 3297453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 330519e6c0fSAndriy Voskoboinyk struct rtwn_tx_data *data = &ring->tx_data[i]; 331519e6c0fSAndriy Voskoboinyk void *desc = (uint8_t *)ring->desc + sc->txdesc_len * i; 3327453645fSAndriy Voskoboinyk 333519e6c0fSAndriy Voskoboinyk rtwn_pci_copy_tx_desc(pc, desc, NULL); 3347453645fSAndriy Voskoboinyk 335519e6c0fSAndriy Voskoboinyk if (data->m != NULL) { 336519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, data->map, 337519e6c0fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 338519e6c0fSAndriy Voskoboinyk bus_dmamap_unload(ring->data_dmat, data->map); 339519e6c0fSAndriy Voskoboinyk m_freem(data->m); 340519e6c0fSAndriy Voskoboinyk data->m = NULL; 3417453645fSAndriy Voskoboinyk } 342519e6c0fSAndriy Voskoboinyk if (data->ni != NULL) { 343519e6c0fSAndriy Voskoboinyk ieee80211_free_node(data->ni); 344519e6c0fSAndriy Voskoboinyk data->ni = NULL; 3457453645fSAndriy Voskoboinyk } 3467453645fSAndriy Voskoboinyk } 3477453645fSAndriy Voskoboinyk 348519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 3497453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 3507453645fSAndriy Voskoboinyk 3517453645fSAndriy Voskoboinyk sc->qfullmsk &= ~(1 << qid); 352519e6c0fSAndriy Voskoboinyk ring->queued = 0; 353519e6c0fSAndriy Voskoboinyk ring->last = ring->cur = 0; 354519e6c0fSAndriy Voskoboinyk } 355519e6c0fSAndriy Voskoboinyk 356519e6c0fSAndriy Voskoboinyk /* 357519e6c0fSAndriy Voskoboinyk * Clear entry 0 (or 1) in the beacon queue (other are not used). 358519e6c0fSAndriy Voskoboinyk */ 359519e6c0fSAndriy Voskoboinyk static void 360519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(struct rtwn_softc *sc, int id) 361519e6c0fSAndriy Voskoboinyk { 362519e6c0fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 363519e6c0fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE]; 364519e6c0fSAndriy Voskoboinyk struct rtwn_tx_data *data = &ring->tx_data[id]; 365519e6c0fSAndriy Voskoboinyk struct rtwn_tx_desc_common *txd = (struct rtwn_tx_desc_common *) 366519e6c0fSAndriy Voskoboinyk ((uint8_t *)ring->desc + id * sc->txdesc_len); 367519e6c0fSAndriy Voskoboinyk 368519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); 369519e6c0fSAndriy Voskoboinyk if (txd->flags0 & RTWN_FLAGS0_OWN) { 370519e6c0fSAndriy Voskoboinyk /* Clear OWN bit. */ 371519e6c0fSAndriy Voskoboinyk txd->flags0 &= ~RTWN_FLAGS0_OWN; 372519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 373519e6c0fSAndriy Voskoboinyk BUS_DMASYNC_PREWRITE); 374519e6c0fSAndriy Voskoboinyk 375519e6c0fSAndriy Voskoboinyk /* Unload mbuf. */ 376519e6c0fSAndriy Voskoboinyk bus_dmamap_sync(ring->data_dmat, data->map, 377519e6c0fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 378519e6c0fSAndriy Voskoboinyk bus_dmamap_unload(ring->data_dmat, data->map); 379519e6c0fSAndriy Voskoboinyk } 380519e6c0fSAndriy Voskoboinyk } 381519e6c0fSAndriy Voskoboinyk 382519e6c0fSAndriy Voskoboinyk /* 383519e6c0fSAndriy Voskoboinyk * Drop stale entries from Tx ring before the vap will be deleted. 384519e6c0fSAndriy Voskoboinyk * In case if vap is NULL just free everything and reset cur / last pointers. 385519e6c0fSAndriy Voskoboinyk */ 386519e6c0fSAndriy Voskoboinyk static void 387519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_list(struct rtwn_softc *sc, struct ieee80211vap *vap, 388519e6c0fSAndriy Voskoboinyk int qid) 389519e6c0fSAndriy Voskoboinyk { 390519e6c0fSAndriy Voskoboinyk int i; 391519e6c0fSAndriy Voskoboinyk 392519e6c0fSAndriy Voskoboinyk if (vap == NULL) { 393519e6c0fSAndriy Voskoboinyk if (qid != RTWN_PCI_BEACON_QUEUE) { 394519e6c0fSAndriy Voskoboinyk /* 395519e6c0fSAndriy Voskoboinyk * Device was stopped; just clear all entries. 396519e6c0fSAndriy Voskoboinyk */ 397519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_ring_stopped(sc, qid); 398519e6c0fSAndriy Voskoboinyk } else { 399519e6c0fSAndriy Voskoboinyk for (i = 0; i < RTWN_PORT_COUNT; i++) 400519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(sc, i); 401519e6c0fSAndriy Voskoboinyk } 402519e6c0fSAndriy Voskoboinyk } else if (qid == RTWN_PCI_BEACON_QUEUE && 403519e6c0fSAndriy Voskoboinyk (vap->iv_opmode == IEEE80211_M_HOSTAP || 404519e6c0fSAndriy Voskoboinyk vap->iv_opmode == IEEE80211_M_IBSS)) { 405519e6c0fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 406519e6c0fSAndriy Voskoboinyk 407519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(sc, uvp->id); 408519e6c0fSAndriy Voskoboinyk } else { 409519e6c0fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 410519e6c0fSAndriy Voskoboinyk struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; 411519e6c0fSAndriy Voskoboinyk 412519e6c0fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 413519e6c0fSAndriy Voskoboinyk struct rtwn_tx_data *data = &ring->tx_data[i]; 414519e6c0fSAndriy Voskoboinyk if (data->ni != NULL && data->ni->ni_vap == vap) { 415519e6c0fSAndriy Voskoboinyk /* 416519e6c0fSAndriy Voskoboinyk * NB: if some vap is still running 417519e6c0fSAndriy Voskoboinyk * rtwn_pci_tx_done() will free the mbuf; 418519e6c0fSAndriy Voskoboinyk * otherwise, rtwn_stop() will reset all rings 419519e6c0fSAndriy Voskoboinyk * after device shutdown. 420519e6c0fSAndriy Voskoboinyk */ 421519e6c0fSAndriy Voskoboinyk ieee80211_free_node(data->ni); 422519e6c0fSAndriy Voskoboinyk data->ni = NULL; 423519e6c0fSAndriy Voskoboinyk } 424519e6c0fSAndriy Voskoboinyk } 425519e6c0fSAndriy Voskoboinyk } 4267453645fSAndriy Voskoboinyk } 4277453645fSAndriy Voskoboinyk 4287453645fSAndriy Voskoboinyk static void 4297453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(struct rtwn_softc *sc, int qid) 4307453645fSAndriy Voskoboinyk { 4317453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 4327453645fSAndriy Voskoboinyk struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid]; 4337453645fSAndriy Voskoboinyk struct rtwn_tx_data *tx_data; 4347453645fSAndriy Voskoboinyk int i; 4357453645fSAndriy Voskoboinyk 4367453645fSAndriy Voskoboinyk if (tx_ring->desc_dmat != NULL) { 4377453645fSAndriy Voskoboinyk if (tx_ring->desc != NULL) { 4387453645fSAndriy Voskoboinyk bus_dmamap_sync(tx_ring->desc_dmat, 4397453645fSAndriy Voskoboinyk tx_ring->desc_map, BUS_DMASYNC_POSTWRITE); 4407453645fSAndriy Voskoboinyk bus_dmamap_unload(tx_ring->desc_dmat, 4417453645fSAndriy Voskoboinyk tx_ring->desc_map); 4427453645fSAndriy Voskoboinyk bus_dmamem_free(tx_ring->desc_dmat, tx_ring->desc, 4437453645fSAndriy Voskoboinyk tx_ring->desc_map); 4447453645fSAndriy Voskoboinyk } 4457453645fSAndriy Voskoboinyk bus_dma_tag_destroy(tx_ring->desc_dmat); 4467453645fSAndriy Voskoboinyk } 4477453645fSAndriy Voskoboinyk 4487453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) { 4497453645fSAndriy Voskoboinyk tx_data = &tx_ring->tx_data[i]; 4507453645fSAndriy Voskoboinyk 4517453645fSAndriy Voskoboinyk if (tx_data->m != NULL) { 4527453645fSAndriy Voskoboinyk bus_dmamap_sync(tx_ring->data_dmat, tx_data->map, 4537453645fSAndriy Voskoboinyk BUS_DMASYNC_POSTWRITE); 4547453645fSAndriy Voskoboinyk bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); 4557453645fSAndriy Voskoboinyk m_freem(tx_data->m); 4567453645fSAndriy Voskoboinyk tx_data->m = NULL; 4577453645fSAndriy Voskoboinyk } 4587453645fSAndriy Voskoboinyk } 4597453645fSAndriy Voskoboinyk if (tx_ring->data_dmat != NULL) { 4607453645fSAndriy Voskoboinyk bus_dma_tag_destroy(tx_ring->data_dmat); 4617453645fSAndriy Voskoboinyk tx_ring->data_dmat = NULL; 4627453645fSAndriy Voskoboinyk } 4637453645fSAndriy Voskoboinyk 4647453645fSAndriy Voskoboinyk sc->qfullmsk &= ~(1 << qid); 4657453645fSAndriy Voskoboinyk tx_ring->queued = 0; 4667453645fSAndriy Voskoboinyk tx_ring->last = tx_ring->cur = 0; 4677453645fSAndriy Voskoboinyk } 4687453645fSAndriy Voskoboinyk 4697453645fSAndriy Voskoboinyk static void 4707453645fSAndriy Voskoboinyk rtwn_pci_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap) 4717453645fSAndriy Voskoboinyk { 4727453645fSAndriy Voskoboinyk int i; 4737453645fSAndriy Voskoboinyk 4747453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) 4757453645fSAndriy Voskoboinyk rtwn_pci_reset_tx_list(sc, vap, i); 4767453645fSAndriy Voskoboinyk 4777453645fSAndriy Voskoboinyk if (vap == NULL) { 4787453645fSAndriy Voskoboinyk sc->qfullmsk = 0; 4797453645fSAndriy Voskoboinyk rtwn_pci_reset_rx_list(sc); 4807453645fSAndriy Voskoboinyk } 4817453645fSAndriy Voskoboinyk } 4827453645fSAndriy Voskoboinyk 4837453645fSAndriy Voskoboinyk static int 4847453645fSAndriy Voskoboinyk rtwn_pci_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf, 4857453645fSAndriy Voskoboinyk uint16_t reg, int mlen) 4867453645fSAndriy Voskoboinyk { 4877453645fSAndriy Voskoboinyk int i; 4887453645fSAndriy Voskoboinyk 4897453645fSAndriy Voskoboinyk for (i = 0; i < mlen; i++) 4907453645fSAndriy Voskoboinyk rtwn_pci_write_1(sc, reg++, buf[i]); 4917453645fSAndriy Voskoboinyk 4927453645fSAndriy Voskoboinyk /* NB: cannot fail */ 4937453645fSAndriy Voskoboinyk return (0); 4947453645fSAndriy Voskoboinyk } 4957453645fSAndriy Voskoboinyk 4967453645fSAndriy Voskoboinyk static uint16_t 4977453645fSAndriy Voskoboinyk rtwn_pci_get_qmap(struct rtwn_softc *sc) 4987453645fSAndriy Voskoboinyk { 4997453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 5007453645fSAndriy Voskoboinyk 5017453645fSAndriy Voskoboinyk KASSERT(pc->pc_qmap != 0, ("%s: qmap is not set!\n", __func__)); 5027453645fSAndriy Voskoboinyk 5037453645fSAndriy Voskoboinyk return (pc->pc_qmap); 5047453645fSAndriy Voskoboinyk } 5057453645fSAndriy Voskoboinyk 5067453645fSAndriy Voskoboinyk static void 5077453645fSAndriy Voskoboinyk rtwn_pci_set_desc_addr(struct rtwn_softc *sc) 5087453645fSAndriy Voskoboinyk { 5097453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); 5107453645fSAndriy Voskoboinyk 5117453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RESET, "%s: addresses:\n" 5127453645fSAndriy Voskoboinyk "bk: %08jX, be: %08jX, vi: %08jX, vo: %08jX\n" 5137453645fSAndriy Voskoboinyk "bcn: %08jX, mgt: %08jX, high: %08jX, rx: %08jX\n", 5147453645fSAndriy Voskoboinyk __func__, (uintmax_t)pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr, 5157453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr, 5167453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr, 5177453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr, 5187453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr, 5197453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr, 5207453645fSAndriy Voskoboinyk (uintmax_t)pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr, 5217453645fSAndriy Voskoboinyk (uintmax_t)pc->rx_ring.paddr); 5227453645fSAndriy Voskoboinyk 5237453645fSAndriy Voskoboinyk /* Set Tx Configuration Register. */ 5247453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_TCR, pc->tcr); 5257453645fSAndriy Voskoboinyk 5267453645fSAndriy Voskoboinyk /* Configure Tx DMA. */ 5277453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_BKQ_DESA, 5287453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr); 5297453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_BEQ_DESA, 5307453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr); 5317453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_VIQ_DESA, 5327453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr); 5337453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_VOQ_DESA, 5347453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr); 5357453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_BCNQ_DESA, 5367453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr); 5377453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_MGQ_DESA, 5387453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr); 5397453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_HQ_DESA, 5407453645fSAndriy Voskoboinyk pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr); 5417453645fSAndriy Voskoboinyk 5427453645fSAndriy Voskoboinyk /* Configure Rx DMA. */ 5437453645fSAndriy Voskoboinyk rtwn_pci_write_4(sc, R92C_RX_DESA, pc->rx_ring.paddr); 5447453645fSAndriy Voskoboinyk } 5457453645fSAndriy Voskoboinyk 5467453645fSAndriy Voskoboinyk static void 547d067ef0fSAndriy Voskoboinyk rtwn_pci_beacon_update_begin(struct rtwn_softc *sc, struct ieee80211vap *vap) 548d067ef0fSAndriy Voskoboinyk { 549d067ef0fSAndriy Voskoboinyk struct rtwn_vap *rvp = RTWN_VAP(vap); 550d067ef0fSAndriy Voskoboinyk 551d067ef0fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 552d067ef0fSAndriy Voskoboinyk 553d067ef0fSAndriy Voskoboinyk rtwn_beacon_enable(sc, rvp->id, 0); 554d067ef0fSAndriy Voskoboinyk } 555d067ef0fSAndriy Voskoboinyk 556d067ef0fSAndriy Voskoboinyk static void 557d067ef0fSAndriy Voskoboinyk rtwn_pci_beacon_update_end(struct rtwn_softc *sc, struct ieee80211vap *vap) 558d067ef0fSAndriy Voskoboinyk { 559d067ef0fSAndriy Voskoboinyk struct rtwn_vap *rvp = RTWN_VAP(vap); 560d067ef0fSAndriy Voskoboinyk 561d067ef0fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 562d067ef0fSAndriy Voskoboinyk 563d067ef0fSAndriy Voskoboinyk if (rvp->curr_mode != R92C_MSR_NOLINK) 564d067ef0fSAndriy Voskoboinyk rtwn_beacon_enable(sc, rvp->id, 1); 565d067ef0fSAndriy Voskoboinyk } 566d067ef0fSAndriy Voskoboinyk 567d067ef0fSAndriy Voskoboinyk static void 5687453645fSAndriy Voskoboinyk rtwn_pci_attach_methods(struct rtwn_softc *sc) 5697453645fSAndriy Voskoboinyk { 5707453645fSAndriy Voskoboinyk sc->sc_write_1 = rtwn_pci_write_1; 5717453645fSAndriy Voskoboinyk sc->sc_write_2 = rtwn_pci_write_2; 5727453645fSAndriy Voskoboinyk sc->sc_write_4 = rtwn_pci_write_4; 5737453645fSAndriy Voskoboinyk sc->sc_read_1 = rtwn_pci_read_1; 5747453645fSAndriy Voskoboinyk sc->sc_read_2 = rtwn_pci_read_2; 5757453645fSAndriy Voskoboinyk sc->sc_read_4 = rtwn_pci_read_4; 5767453645fSAndriy Voskoboinyk sc->sc_delay = rtwn_pci_delay; 5777453645fSAndriy Voskoboinyk sc->sc_tx_start = rtwn_pci_tx_start; 5787453645fSAndriy Voskoboinyk sc->sc_reset_lists = rtwn_pci_reset_lists; 5797453645fSAndriy Voskoboinyk sc->sc_abort_xfers = rtwn_nop_softc; 5807453645fSAndriy Voskoboinyk sc->sc_fw_write_block = rtwn_pci_fw_write_block; 5817453645fSAndriy Voskoboinyk sc->sc_get_qmap = rtwn_pci_get_qmap; 5827453645fSAndriy Voskoboinyk sc->sc_set_desc_addr = rtwn_pci_set_desc_addr; 5837453645fSAndriy Voskoboinyk sc->sc_drop_incorrect_tx = rtwn_nop_softc; 584d067ef0fSAndriy Voskoboinyk sc->sc_beacon_update_begin = rtwn_pci_beacon_update_begin; 585d067ef0fSAndriy Voskoboinyk sc->sc_beacon_update_end = rtwn_pci_beacon_update_end; 586d067ef0fSAndriy Voskoboinyk sc->sc_beacon_unload = rtwn_pci_reset_beacon_ring; 587d067ef0fSAndriy Voskoboinyk 588d067ef0fSAndriy Voskoboinyk sc->bcn_check_interval = 25000; 5897453645fSAndriy Voskoboinyk } 5907453645fSAndriy Voskoboinyk 5917453645fSAndriy Voskoboinyk static int 5927453645fSAndriy Voskoboinyk rtwn_pci_attach(device_t dev) 5937453645fSAndriy Voskoboinyk { 5947453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(dev); 5957453645fSAndriy Voskoboinyk struct rtwn_softc *sc = &pc->pc_sc; 5967453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 5977453645fSAndriy Voskoboinyk uint32_t lcsr; 5987453645fSAndriy Voskoboinyk int cap_off, i, error, rid; 5997453645fSAndriy Voskoboinyk 6007453645fSAndriy Voskoboinyk if (matched_chip >= RTWN_CHIP_MAX_PCI) 6017453645fSAndriy Voskoboinyk return (ENXIO); 6027453645fSAndriy Voskoboinyk 6037453645fSAndriy Voskoboinyk /* 6047453645fSAndriy Voskoboinyk * Get the offset of the PCI Express Capability Structure in PCI 6057453645fSAndriy Voskoboinyk * Configuration Space. 6067453645fSAndriy Voskoboinyk */ 6077453645fSAndriy Voskoboinyk error = pci_find_cap(dev, PCIY_EXPRESS, &cap_off); 6087453645fSAndriy Voskoboinyk if (error != 0) { 6097453645fSAndriy Voskoboinyk device_printf(dev, "PCIe capability structure not found!\n"); 6107453645fSAndriy Voskoboinyk return (error); 6117453645fSAndriy Voskoboinyk } 6127453645fSAndriy Voskoboinyk 6137453645fSAndriy Voskoboinyk /* Enable bus-mastering. */ 6147453645fSAndriy Voskoboinyk pci_enable_busmaster(dev); 6157453645fSAndriy Voskoboinyk 6167453645fSAndriy Voskoboinyk rid = PCIR_BAR(2); 6177453645fSAndriy Voskoboinyk pc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 6187453645fSAndriy Voskoboinyk RF_ACTIVE); 6197453645fSAndriy Voskoboinyk if (pc->mem == NULL) { 6207453645fSAndriy Voskoboinyk device_printf(dev, "can't map mem space\n"); 6217453645fSAndriy Voskoboinyk return (ENOMEM); 6227453645fSAndriy Voskoboinyk } 6237453645fSAndriy Voskoboinyk pc->pc_st = rman_get_bustag(pc->mem); 6247453645fSAndriy Voskoboinyk pc->pc_sh = rman_get_bushandle(pc->mem); 6257453645fSAndriy Voskoboinyk 6267453645fSAndriy Voskoboinyk /* Install interrupt handler. */ 6277453645fSAndriy Voskoboinyk rid = 1; 6287453645fSAndriy Voskoboinyk if (pci_alloc_msi(dev, &rid) == 0) 6297453645fSAndriy Voskoboinyk rid = 1; 6307453645fSAndriy Voskoboinyk else 6317453645fSAndriy Voskoboinyk rid = 0; 6327453645fSAndriy Voskoboinyk pc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | 6337453645fSAndriy Voskoboinyk (rid != 0 ? 0 : RF_SHAREABLE)); 6347453645fSAndriy Voskoboinyk if (pc->irq == NULL) { 6357453645fSAndriy Voskoboinyk device_printf(dev, "can't map interrupt\n"); 6367453645fSAndriy Voskoboinyk goto detach; 6377453645fSAndriy Voskoboinyk } 6387453645fSAndriy Voskoboinyk 6397453645fSAndriy Voskoboinyk /* Disable PCIe Active State Power Management (ASPM). */ 6407453645fSAndriy Voskoboinyk lcsr = pci_read_config(dev, cap_off + PCIER_LINK_CTL, 4); 6417453645fSAndriy Voskoboinyk lcsr &= ~PCIEM_LINK_CTL_ASPMC; 6427453645fSAndriy Voskoboinyk pci_write_config(dev, cap_off + PCIER_LINK_CTL, lcsr, 4); 6437453645fSAndriy Voskoboinyk 6447453645fSAndriy Voskoboinyk sc->sc_dev = dev; 6457453645fSAndriy Voskoboinyk ic->ic_name = device_get_nameunit(dev); 6467453645fSAndriy Voskoboinyk 6477453645fSAndriy Voskoboinyk /* Need to be initialized early. */ 6487453645fSAndriy Voskoboinyk rtwn_sysctlattach(sc); 6497453645fSAndriy Voskoboinyk mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF); 6507453645fSAndriy Voskoboinyk 6517453645fSAndriy Voskoboinyk rtwn_pci_attach_methods(sc); 6527453645fSAndriy Voskoboinyk /* XXX something similar to USB_GET_DRIVER_INFO() */ 6537453645fSAndriy Voskoboinyk rtwn_pci_attach_private(pc, matched_chip); 6547453645fSAndriy Voskoboinyk 6557453645fSAndriy Voskoboinyk /* Allocate Tx/Rx buffers. */ 6567453645fSAndriy Voskoboinyk error = rtwn_pci_alloc_rx_list(sc); 6577453645fSAndriy Voskoboinyk if (error != 0) { 6587453645fSAndriy Voskoboinyk device_printf(dev, 6597453645fSAndriy Voskoboinyk "could not allocate Rx buffers, error %d\n", 6607453645fSAndriy Voskoboinyk error); 6617453645fSAndriy Voskoboinyk goto detach; 6627453645fSAndriy Voskoboinyk } 6637453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) { 6647453645fSAndriy Voskoboinyk error = rtwn_pci_alloc_tx_list(sc, i); 6657453645fSAndriy Voskoboinyk if (error != 0) { 6667453645fSAndriy Voskoboinyk device_printf(dev, 6677453645fSAndriy Voskoboinyk "could not allocate Tx buffers, error %d\n", 6687453645fSAndriy Voskoboinyk error); 6697453645fSAndriy Voskoboinyk goto detach; 6707453645fSAndriy Voskoboinyk } 6717453645fSAndriy Voskoboinyk } 6727453645fSAndriy Voskoboinyk 6737453645fSAndriy Voskoboinyk /* Generic attach. */ 6747453645fSAndriy Voskoboinyk error = rtwn_attach(sc); 6757453645fSAndriy Voskoboinyk if (error != 0) 6767453645fSAndriy Voskoboinyk goto detach; 6777453645fSAndriy Voskoboinyk 6787453645fSAndriy Voskoboinyk /* 6797453645fSAndriy Voskoboinyk * Hook our interrupt after all initialization is complete. 6807453645fSAndriy Voskoboinyk */ 6817453645fSAndriy Voskoboinyk error = bus_setup_intr(dev, pc->irq, INTR_TYPE_NET | INTR_MPSAFE, 6827453645fSAndriy Voskoboinyk NULL, rtwn_pci_intr, sc, &pc->pc_ih); 6837453645fSAndriy Voskoboinyk if (error != 0) { 6847453645fSAndriy Voskoboinyk device_printf(dev, "can't establish interrupt, error %d\n", 6857453645fSAndriy Voskoboinyk error); 6867453645fSAndriy Voskoboinyk goto detach; 6877453645fSAndriy Voskoboinyk } 6887453645fSAndriy Voskoboinyk 6897453645fSAndriy Voskoboinyk return (0); 6907453645fSAndriy Voskoboinyk 6917453645fSAndriy Voskoboinyk detach: 6927453645fSAndriy Voskoboinyk rtwn_pci_detach(dev); /* failure */ 6937453645fSAndriy Voskoboinyk return (ENXIO); 6947453645fSAndriy Voskoboinyk } 6957453645fSAndriy Voskoboinyk 6967453645fSAndriy Voskoboinyk static int 6977453645fSAndriy Voskoboinyk rtwn_pci_detach(device_t dev) 6987453645fSAndriy Voskoboinyk { 6997453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(dev); 7007453645fSAndriy Voskoboinyk struct rtwn_softc *sc = &pc->pc_sc; 7017453645fSAndriy Voskoboinyk int i; 7027453645fSAndriy Voskoboinyk 7037453645fSAndriy Voskoboinyk /* Generic detach. */ 7047453645fSAndriy Voskoboinyk rtwn_detach(sc); 7057453645fSAndriy Voskoboinyk 7067453645fSAndriy Voskoboinyk /* Uninstall interrupt handler. */ 7077453645fSAndriy Voskoboinyk if (pc->irq != NULL) { 7087453645fSAndriy Voskoboinyk bus_teardown_intr(dev, pc->irq, pc->pc_ih); 7097453645fSAndriy Voskoboinyk bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(pc->irq), 7107453645fSAndriy Voskoboinyk pc->irq); 7117453645fSAndriy Voskoboinyk pci_release_msi(dev); 7127453645fSAndriy Voskoboinyk } 7137453645fSAndriy Voskoboinyk 7147453645fSAndriy Voskoboinyk /* Free Tx/Rx buffers. */ 7157453645fSAndriy Voskoboinyk for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) 7167453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(sc, i); 7177453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(sc); 7187453645fSAndriy Voskoboinyk 7197453645fSAndriy Voskoboinyk if (pc->mem != NULL) 7207453645fSAndriy Voskoboinyk bus_release_resource(dev, SYS_RES_MEMORY, 7217453645fSAndriy Voskoboinyk rman_get_rid(pc->mem), pc->mem); 7227453645fSAndriy Voskoboinyk 7237453645fSAndriy Voskoboinyk rtwn_detach_private(sc); 7247453645fSAndriy Voskoboinyk mtx_destroy(&sc->sc_mtx); 7257453645fSAndriy Voskoboinyk 7267453645fSAndriy Voskoboinyk return (0); 7277453645fSAndriy Voskoboinyk } 7287453645fSAndriy Voskoboinyk 7297453645fSAndriy Voskoboinyk static int 7307453645fSAndriy Voskoboinyk rtwn_pci_shutdown(device_t self) 7317453645fSAndriy Voskoboinyk { 7327453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(self); 7337453645fSAndriy Voskoboinyk 7347453645fSAndriy Voskoboinyk ieee80211_stop_all(&pc->pc_sc.sc_ic); 7357453645fSAndriy Voskoboinyk return (0); 7367453645fSAndriy Voskoboinyk } 7377453645fSAndriy Voskoboinyk 7387453645fSAndriy Voskoboinyk static int 7397453645fSAndriy Voskoboinyk rtwn_pci_suspend(device_t self) 7407453645fSAndriy Voskoboinyk { 7417453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(self); 7427453645fSAndriy Voskoboinyk 7437453645fSAndriy Voskoboinyk rtwn_suspend(&pc->pc_sc); 7447453645fSAndriy Voskoboinyk 7457453645fSAndriy Voskoboinyk return (0); 7467453645fSAndriy Voskoboinyk } 7477453645fSAndriy Voskoboinyk 7487453645fSAndriy Voskoboinyk static int 7497453645fSAndriy Voskoboinyk rtwn_pci_resume(device_t self) 7507453645fSAndriy Voskoboinyk { 7517453645fSAndriy Voskoboinyk struct rtwn_pci_softc *pc = device_get_softc(self); 7527453645fSAndriy Voskoboinyk 7537453645fSAndriy Voskoboinyk rtwn_resume(&pc->pc_sc); 7547453645fSAndriy Voskoboinyk 7557453645fSAndriy Voskoboinyk return (0); 7567453645fSAndriy Voskoboinyk } 7577453645fSAndriy Voskoboinyk 7587453645fSAndriy Voskoboinyk static device_method_t rtwn_pci_methods[] = { 7597453645fSAndriy Voskoboinyk /* Device interface */ 7607453645fSAndriy Voskoboinyk DEVMETHOD(device_probe, rtwn_pci_probe), 7617453645fSAndriy Voskoboinyk DEVMETHOD(device_attach, rtwn_pci_attach), 7627453645fSAndriy Voskoboinyk DEVMETHOD(device_detach, rtwn_pci_detach), 7637453645fSAndriy Voskoboinyk DEVMETHOD(device_shutdown, rtwn_pci_shutdown), 7647453645fSAndriy Voskoboinyk DEVMETHOD(device_suspend, rtwn_pci_suspend), 7657453645fSAndriy Voskoboinyk DEVMETHOD(device_resume, rtwn_pci_resume), 7667453645fSAndriy Voskoboinyk 7677453645fSAndriy Voskoboinyk DEVMETHOD_END 7687453645fSAndriy Voskoboinyk }; 7697453645fSAndriy Voskoboinyk 7707453645fSAndriy Voskoboinyk static driver_t rtwn_pci_driver = { 7717453645fSAndriy Voskoboinyk "rtwn", 7727453645fSAndriy Voskoboinyk rtwn_pci_methods, 7737453645fSAndriy Voskoboinyk sizeof(struct rtwn_pci_softc) 7747453645fSAndriy Voskoboinyk }; 7757453645fSAndriy Voskoboinyk 7767453645fSAndriy Voskoboinyk static devclass_t rtwn_pci_devclass; 7777453645fSAndriy Voskoboinyk 7787453645fSAndriy Voskoboinyk DRIVER_MODULE(rtwn_pci, pci, rtwn_pci_driver, rtwn_pci_devclass, NULL, NULL); 7797453645fSAndriy Voskoboinyk MODULE_VERSION(rtwn_pci, 1); 7807453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, pci, 1, 1, 1); 7817453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, wlan, 1, 1, 1); 7827453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, rtwn, 2, 2, 2); 783