112e36acbSWarner Losh /* 212e36acbSWarner Losh * Copyright (c) 2007 The DragonFly Project. All rights reserved. 312e36acbSWarner Losh * 412e36acbSWarner Losh * This code is derived from software contributed to The DragonFly Project 512e36acbSWarner Losh * by Sepherosa Ziehau <sepherosa@gmail.com> 612e36acbSWarner Losh * 712e36acbSWarner Losh * Redistribution and use in source and binary forms, with or without 812e36acbSWarner Losh * modification, are permitted provided that the following conditions 912e36acbSWarner Losh * are met: 1012e36acbSWarner Losh * 1112e36acbSWarner Losh * 1. Redistributions of source code must retain the above copyright 1212e36acbSWarner Losh * notice, this list of conditions and the following disclaimer. 1312e36acbSWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 1412e36acbSWarner Losh * notice, this list of conditions and the following disclaimer in 1512e36acbSWarner Losh * the documentation and/or other materials provided with the 1612e36acbSWarner Losh * distribution. 1712e36acbSWarner Losh * 3. Neither the name of The DragonFly Project nor the names of its 1812e36acbSWarner Losh * contributors may be used to endorse or promote products derived 1912e36acbSWarner Losh * from this software without specific, prior written permission. 2012e36acbSWarner Losh * 2112e36acbSWarner Losh * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2212e36acbSWarner Losh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2312e36acbSWarner Losh * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2412e36acbSWarner Losh * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2512e36acbSWarner Losh * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2612e36acbSWarner Losh * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2712e36acbSWarner Losh * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2812e36acbSWarner Losh * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2912e36acbSWarner Losh * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3012e36acbSWarner Losh * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3112e36acbSWarner Losh * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3212e36acbSWarner Losh * SUCH DAMAGE. 3312e36acbSWarner Losh * 3412e36acbSWarner Losh * $DragonFly: src/sys/dev/netif/bwi/if_bwi.c,v 1.19 2008/02/15 11:15:38 sephe Exp $ 3512e36acbSWarner Losh */ 3612e36acbSWarner Losh 3712e36acbSWarner Losh #include <sys/cdefs.h> 3812e36acbSWarner Losh __FBSDID("$FreeBSD$"); 3912e36acbSWarner Losh 4012e36acbSWarner Losh #include "opt_inet.h" 4112e36acbSWarner Losh #include "opt_bwi.h" 4212e36acbSWarner Losh 4312e36acbSWarner Losh #include <sys/param.h> 4412e36acbSWarner Losh #include <sys/endian.h> 4512e36acbSWarner Losh #include <sys/kernel.h> 4612e36acbSWarner Losh #include <sys/bus.h> 4712e36acbSWarner Losh #include <sys/malloc.h> 4812e36acbSWarner Losh #include <sys/proc.h> 4912e36acbSWarner Losh #include <sys/rman.h> 5012e36acbSWarner Losh #include <sys/socket.h> 5112e36acbSWarner Losh #include <sys/sockio.h> 5212e36acbSWarner Losh #include <sys/sysctl.h> 5312e36acbSWarner Losh #include <sys/systm.h> 5412e36acbSWarner Losh #include <sys/taskqueue.h> 5512e36acbSWarner Losh 5612e36acbSWarner Losh #include <net/if.h> 5712e36acbSWarner Losh #include <net/if_dl.h> 5812e36acbSWarner Losh #include <net/if_media.h> 5912e36acbSWarner Losh #include <net/if_types.h> 6012e36acbSWarner Losh #include <net/if_arp.h> 6112e36acbSWarner Losh #include <net/ethernet.h> 6212e36acbSWarner Losh #include <net/if_llc.h> 6312e36acbSWarner Losh 6412e36acbSWarner Losh #include <net80211/ieee80211_var.h> 6512e36acbSWarner Losh #include <net80211/ieee80211_radiotap.h> 6612e36acbSWarner Losh #include <net80211/ieee80211_regdomain.h> 6712e36acbSWarner Losh #include <net80211/ieee80211_phy.h> 68b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h> 6912e36acbSWarner Losh 7012e36acbSWarner Losh #include <net/bpf.h> 7112e36acbSWarner Losh 7212e36acbSWarner Losh #ifdef INET 7312e36acbSWarner Losh #include <netinet/in.h> 7412e36acbSWarner Losh #include <netinet/if_ether.h> 7512e36acbSWarner Losh #endif 7612e36acbSWarner Losh 7712e36acbSWarner Losh #include <machine/bus.h> 7812e36acbSWarner Losh 7912e36acbSWarner Losh #include <dev/pci/pcivar.h> 8012e36acbSWarner Losh #include <dev/pci/pcireg.h> 8112e36acbSWarner Losh 8212e36acbSWarner Losh #include <dev/bwi/bitops.h> 8312e36acbSWarner Losh #include <dev/bwi/if_bwireg.h> 8412e36acbSWarner Losh #include <dev/bwi/if_bwivar.h> 8512e36acbSWarner Losh #include <dev/bwi/bwimac.h> 8612e36acbSWarner Losh #include <dev/bwi/bwirf.h> 8712e36acbSWarner Losh 8812e36acbSWarner Losh struct bwi_clock_freq { 8912e36acbSWarner Losh u_int clkfreq_min; 9012e36acbSWarner Losh u_int clkfreq_max; 9112e36acbSWarner Losh }; 9212e36acbSWarner Losh 9312e36acbSWarner Losh struct bwi_myaddr_bssid { 9412e36acbSWarner Losh uint8_t myaddr[IEEE80211_ADDR_LEN]; 9512e36acbSWarner Losh uint8_t bssid[IEEE80211_ADDR_LEN]; 9612e36acbSWarner Losh } __packed; 9712e36acbSWarner Losh 9812e36acbSWarner Losh static struct ieee80211vap *bwi_vap_create(struct ieee80211com *, 9912e36acbSWarner Losh const char [IFNAMSIZ], int, int, int, 10012e36acbSWarner Losh const uint8_t [IEEE80211_ADDR_LEN], 10112e36acbSWarner Losh const uint8_t [IEEE80211_ADDR_LEN]); 10212e36acbSWarner Losh static void bwi_vap_delete(struct ieee80211vap *); 10312e36acbSWarner Losh static void bwi_init(void *); 10412e36acbSWarner Losh static int bwi_ioctl(struct ifnet *, u_long, caddr_t); 10512e36acbSWarner Losh static void bwi_start(struct ifnet *); 10612e36acbSWarner Losh static void bwi_start_locked(struct ifnet *); 10712e36acbSWarner Losh static int bwi_raw_xmit(struct ieee80211_node *, struct mbuf *, 10812e36acbSWarner Losh const struct ieee80211_bpf_params *); 10958fbe5abSJohn Baldwin static void bwi_watchdog(void *); 11012e36acbSWarner Losh static void bwi_scan_start(struct ieee80211com *); 11112e36acbSWarner Losh static void bwi_set_channel(struct ieee80211com *); 11212e36acbSWarner Losh static void bwi_scan_end(struct ieee80211com *); 11312e36acbSWarner Losh static int bwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); 11412e36acbSWarner Losh static void bwi_updateslot(struct ifnet *); 11512e36acbSWarner Losh static int bwi_media_change(struct ifnet *); 11612e36acbSWarner Losh 11712e36acbSWarner Losh static void bwi_calibrate(void *); 11812e36acbSWarner Losh 11912e36acbSWarner Losh static int bwi_calc_rssi(struct bwi_softc *, const struct bwi_rxbuf_hdr *); 12012e36acbSWarner Losh static int bwi_calc_noise(struct bwi_softc *); 12112e36acbSWarner Losh static __inline uint8_t bwi_ofdm_plcp2rate(const uint32_t *); 12212e36acbSWarner Losh static __inline uint8_t bwi_ds_plcp2rate(const struct ieee80211_ds_plcp_hdr *); 1235463c4a4SSam Leffler static void bwi_rx_radiotap(struct bwi_softc *, struct mbuf *, 12412e36acbSWarner Losh struct bwi_rxbuf_hdr *, const void *, int, int, int); 12512e36acbSWarner Losh 12612e36acbSWarner Losh static void bwi_restart(void *, int); 12712e36acbSWarner Losh static void bwi_init_statechg(struct bwi_softc *, int); 12812e36acbSWarner Losh static void bwi_stop(struct bwi_softc *, int); 12912e36acbSWarner Losh static void bwi_stop_locked(struct bwi_softc *, int); 13012e36acbSWarner Losh static int bwi_newbuf(struct bwi_softc *, int, int); 13112e36acbSWarner Losh static int bwi_encap(struct bwi_softc *, int, struct mbuf *, 13212e36acbSWarner Losh struct ieee80211_node *); 13312e36acbSWarner Losh static int bwi_encap_raw(struct bwi_softc *, int, struct mbuf *, 13412e36acbSWarner Losh struct ieee80211_node *, 13512e36acbSWarner Losh const struct ieee80211_bpf_params *); 13612e36acbSWarner Losh 13712e36acbSWarner Losh static void bwi_init_rxdesc_ring32(struct bwi_softc *, uint32_t, 13812e36acbSWarner Losh bus_addr_t, int, int); 13912e36acbSWarner Losh static void bwi_reset_rx_ring32(struct bwi_softc *, uint32_t); 14012e36acbSWarner Losh 14112e36acbSWarner Losh static int bwi_init_tx_ring32(struct bwi_softc *, int); 14212e36acbSWarner Losh static int bwi_init_rx_ring32(struct bwi_softc *); 14312e36acbSWarner Losh static int bwi_init_txstats32(struct bwi_softc *); 14412e36acbSWarner Losh static void bwi_free_tx_ring32(struct bwi_softc *, int); 14512e36acbSWarner Losh static void bwi_free_rx_ring32(struct bwi_softc *); 14612e36acbSWarner Losh static void bwi_free_txstats32(struct bwi_softc *); 14712e36acbSWarner Losh static void bwi_setup_rx_desc32(struct bwi_softc *, int, bus_addr_t, int); 14812e36acbSWarner Losh static void bwi_setup_tx_desc32(struct bwi_softc *, struct bwi_ring_data *, 14912e36acbSWarner Losh int, bus_addr_t, int); 15012e36acbSWarner Losh static int bwi_rxeof32(struct bwi_softc *); 15112e36acbSWarner Losh static void bwi_start_tx32(struct bwi_softc *, uint32_t, int); 15212e36acbSWarner Losh static void bwi_txeof_status32(struct bwi_softc *); 15312e36acbSWarner Losh 15412e36acbSWarner Losh static int bwi_init_tx_ring64(struct bwi_softc *, int); 15512e36acbSWarner Losh static int bwi_init_rx_ring64(struct bwi_softc *); 15612e36acbSWarner Losh static int bwi_init_txstats64(struct bwi_softc *); 15712e36acbSWarner Losh static void bwi_free_tx_ring64(struct bwi_softc *, int); 15812e36acbSWarner Losh static void bwi_free_rx_ring64(struct bwi_softc *); 15912e36acbSWarner Losh static void bwi_free_txstats64(struct bwi_softc *); 16012e36acbSWarner Losh static void bwi_setup_rx_desc64(struct bwi_softc *, int, bus_addr_t, int); 16112e36acbSWarner Losh static void bwi_setup_tx_desc64(struct bwi_softc *, struct bwi_ring_data *, 16212e36acbSWarner Losh int, bus_addr_t, int); 16312e36acbSWarner Losh static int bwi_rxeof64(struct bwi_softc *); 16412e36acbSWarner Losh static void bwi_start_tx64(struct bwi_softc *, uint32_t, int); 16512e36acbSWarner Losh static void bwi_txeof_status64(struct bwi_softc *); 16612e36acbSWarner Losh 16712e36acbSWarner Losh static int bwi_rxeof(struct bwi_softc *, int); 16812e36acbSWarner Losh static void _bwi_txeof(struct bwi_softc *, uint16_t, int, int); 16912e36acbSWarner Losh static void bwi_txeof(struct bwi_softc *); 17012e36acbSWarner Losh static void bwi_txeof_status(struct bwi_softc *, int); 17112e36acbSWarner Losh static void bwi_enable_intrs(struct bwi_softc *, uint32_t); 17212e36acbSWarner Losh static void bwi_disable_intrs(struct bwi_softc *, uint32_t); 17312e36acbSWarner Losh 17412e36acbSWarner Losh static int bwi_dma_alloc(struct bwi_softc *); 17512e36acbSWarner Losh static void bwi_dma_free(struct bwi_softc *); 17612e36acbSWarner Losh static int bwi_dma_ring_alloc(struct bwi_softc *, bus_dma_tag_t, 17712e36acbSWarner Losh struct bwi_ring_data *, bus_size_t, 17812e36acbSWarner Losh uint32_t); 17912e36acbSWarner Losh static int bwi_dma_mbuf_create(struct bwi_softc *); 18012e36acbSWarner Losh static void bwi_dma_mbuf_destroy(struct bwi_softc *, int, int); 18112e36acbSWarner Losh static int bwi_dma_txstats_alloc(struct bwi_softc *, uint32_t, bus_size_t); 18212e36acbSWarner Losh static void bwi_dma_txstats_free(struct bwi_softc *); 18312e36acbSWarner Losh static void bwi_dma_ring_addr(void *, bus_dma_segment_t *, int, int); 18412e36acbSWarner Losh static void bwi_dma_buf_addr(void *, bus_dma_segment_t *, int, 18512e36acbSWarner Losh bus_size_t, int); 18612e36acbSWarner Losh 18712e36acbSWarner Losh static void bwi_power_on(struct bwi_softc *, int); 18812e36acbSWarner Losh static int bwi_power_off(struct bwi_softc *, int); 18912e36acbSWarner Losh static int bwi_set_clock_mode(struct bwi_softc *, enum bwi_clock_mode); 19012e36acbSWarner Losh static int bwi_set_clock_delay(struct bwi_softc *); 19112e36acbSWarner Losh static void bwi_get_clock_freq(struct bwi_softc *, struct bwi_clock_freq *); 19212e36acbSWarner Losh static int bwi_get_pwron_delay(struct bwi_softc *sc); 19312e36acbSWarner Losh static void bwi_set_addr_filter(struct bwi_softc *, uint16_t, 19412e36acbSWarner Losh const uint8_t *); 19512e36acbSWarner Losh static void bwi_set_bssid(struct bwi_softc *, const uint8_t *); 19612e36acbSWarner Losh 19712e36acbSWarner Losh static void bwi_get_card_flags(struct bwi_softc *); 19812e36acbSWarner Losh static void bwi_get_eaddr(struct bwi_softc *, uint16_t, uint8_t *); 19912e36acbSWarner Losh 20012e36acbSWarner Losh static int bwi_bus_attach(struct bwi_softc *); 20112e36acbSWarner Losh static int bwi_bbp_attach(struct bwi_softc *); 20212e36acbSWarner Losh static int bwi_bbp_power_on(struct bwi_softc *, enum bwi_clock_mode); 20312e36acbSWarner Losh static void bwi_bbp_power_off(struct bwi_softc *); 20412e36acbSWarner Losh 20512e36acbSWarner Losh static const char *bwi_regwin_name(const struct bwi_regwin *); 20612e36acbSWarner Losh static uint32_t bwi_regwin_disable_bits(struct bwi_softc *); 20712e36acbSWarner Losh static void bwi_regwin_info(struct bwi_softc *, uint16_t *, uint8_t *); 20812e36acbSWarner Losh static int bwi_regwin_select(struct bwi_softc *, int); 20912e36acbSWarner Losh 21012e36acbSWarner Losh static void bwi_led_attach(struct bwi_softc *); 21112e36acbSWarner Losh static void bwi_led_newstate(struct bwi_softc *, enum ieee80211_state); 21212e36acbSWarner Losh static void bwi_led_event(struct bwi_softc *, int); 21312e36acbSWarner Losh static void bwi_led_blink_start(struct bwi_softc *, int, int); 21412e36acbSWarner Losh static void bwi_led_blink_next(void *); 21512e36acbSWarner Losh static void bwi_led_blink_end(void *); 21612e36acbSWarner Losh 21712e36acbSWarner Losh static const struct { 21812e36acbSWarner Losh uint16_t did_min; 21912e36acbSWarner Losh uint16_t did_max; 22012e36acbSWarner Losh uint16_t bbp_id; 22112e36acbSWarner Losh } bwi_bbpid_map[] = { 22212e36acbSWarner Losh { 0x4301, 0x4301, 0x4301 }, 22312e36acbSWarner Losh { 0x4305, 0x4307, 0x4307 }, 22412e36acbSWarner Losh { 0x4403, 0x4403, 0x4402 }, 22512e36acbSWarner Losh { 0x4610, 0x4615, 0x4610 }, 22612e36acbSWarner Losh { 0x4710, 0x4715, 0x4710 }, 22712e36acbSWarner Losh { 0x4720, 0x4725, 0x4309 } 22812e36acbSWarner Losh }; 22912e36acbSWarner Losh 23012e36acbSWarner Losh static const struct { 23112e36acbSWarner Losh uint16_t bbp_id; 23212e36acbSWarner Losh int nregwin; 23312e36acbSWarner Losh } bwi_regwin_count[] = { 23412e36acbSWarner Losh { 0x4301, 5 }, 23512e36acbSWarner Losh { 0x4306, 6 }, 23612e36acbSWarner Losh { 0x4307, 5 }, 23712e36acbSWarner Losh { 0x4310, 8 }, 23812e36acbSWarner Losh { 0x4401, 3 }, 23912e36acbSWarner Losh { 0x4402, 3 }, 24012e36acbSWarner Losh { 0x4610, 9 }, 24112e36acbSWarner Losh { 0x4704, 9 }, 24212e36acbSWarner Losh { 0x4710, 9 }, 24312e36acbSWarner Losh { 0x5365, 7 } 24412e36acbSWarner Losh }; 24512e36acbSWarner Losh 24612e36acbSWarner Losh #define CLKSRC(src) \ 24712e36acbSWarner Losh [BWI_CLKSRC_ ## src] = { \ 24812e36acbSWarner Losh .freq_min = BWI_CLKSRC_ ##src## _FMIN, \ 24912e36acbSWarner Losh .freq_max = BWI_CLKSRC_ ##src## _FMAX \ 25012e36acbSWarner Losh } 25112e36acbSWarner Losh 25212e36acbSWarner Losh static const struct { 25312e36acbSWarner Losh u_int freq_min; 25412e36acbSWarner Losh u_int freq_max; 25512e36acbSWarner Losh } bwi_clkfreq[BWI_CLKSRC_MAX] = { 25612e36acbSWarner Losh CLKSRC(LP_OSC), 25712e36acbSWarner Losh CLKSRC(CS_OSC), 25812e36acbSWarner Losh CLKSRC(PCI) 25912e36acbSWarner Losh }; 26012e36acbSWarner Losh 26112e36acbSWarner Losh #undef CLKSRC 26212e36acbSWarner Losh 26312e36acbSWarner Losh #define VENDOR_LED_ACT(vendor) \ 26412e36acbSWarner Losh { \ 26512e36acbSWarner Losh .vid = PCI_VENDOR_##vendor, \ 26612e36acbSWarner Losh .led_act = { BWI_VENDOR_LED_ACT_##vendor } \ 26712e36acbSWarner Losh } 26812e36acbSWarner Losh 26912e36acbSWarner Losh static const struct { 27012e36acbSWarner Losh #define PCI_VENDOR_COMPAQ 0x0e11 27112e36acbSWarner Losh #define PCI_VENDOR_LINKSYS 0x1737 27212e36acbSWarner Losh uint16_t vid; 27312e36acbSWarner Losh uint8_t led_act[BWI_LED_MAX]; 27412e36acbSWarner Losh } bwi_vendor_led_act[] = { 27512e36acbSWarner Losh VENDOR_LED_ACT(COMPAQ), 27612e36acbSWarner Losh VENDOR_LED_ACT(LINKSYS) 27712e36acbSWarner Losh #undef PCI_VENDOR_LINKSYS 27812e36acbSWarner Losh #undef PCI_VENDOR_COMPAQ 27912e36acbSWarner Losh }; 28012e36acbSWarner Losh 28112e36acbSWarner Losh static const uint8_t bwi_default_led_act[BWI_LED_MAX] = 28212e36acbSWarner Losh { BWI_VENDOR_LED_ACT_DEFAULT }; 28312e36acbSWarner Losh 28412e36acbSWarner Losh #undef VENDOR_LED_ACT 28512e36acbSWarner Losh 28612e36acbSWarner Losh static const struct { 28712e36acbSWarner Losh int on_dur; 28812e36acbSWarner Losh int off_dur; 28912e36acbSWarner Losh } bwi_led_duration[109] = { 29012e36acbSWarner Losh [0] = { 400, 100 }, 29112e36acbSWarner Losh [2] = { 150, 75 }, 29212e36acbSWarner Losh [4] = { 90, 45 }, 29312e36acbSWarner Losh [11] = { 66, 34 }, 29412e36acbSWarner Losh [12] = { 53, 26 }, 29512e36acbSWarner Losh [18] = { 42, 21 }, 29612e36acbSWarner Losh [22] = { 35, 17 }, 29712e36acbSWarner Losh [24] = { 32, 16 }, 29812e36acbSWarner Losh [36] = { 21, 10 }, 29912e36acbSWarner Losh [48] = { 16, 8 }, 30012e36acbSWarner Losh [72] = { 11, 5 }, 30112e36acbSWarner Losh [96] = { 9, 4 }, 30212e36acbSWarner Losh [108] = { 7, 3 } 30312e36acbSWarner Losh }; 30412e36acbSWarner Losh 30512e36acbSWarner Losh #ifdef BWI_DEBUG 30612e36acbSWarner Losh #ifdef BWI_DEBUG_VERBOSE 30712e36acbSWarner Losh static uint32_t bwi_debug = BWI_DBG_ATTACH | BWI_DBG_INIT | BWI_DBG_TXPOWER; 30812e36acbSWarner Losh #else 30912e36acbSWarner Losh static uint32_t bwi_debug; 31012e36acbSWarner Losh #endif 31112e36acbSWarner Losh TUNABLE_INT("hw.bwi.debug", (int *)&bwi_debug); 31212e36acbSWarner Losh #endif /* BWI_DEBUG */ 31312e36acbSWarner Losh 31412e36acbSWarner Losh static const uint8_t bwi_zero_addr[IEEE80211_ADDR_LEN]; 31512e36acbSWarner Losh 31612e36acbSWarner Losh uint16_t 31712e36acbSWarner Losh bwi_read_sprom(struct bwi_softc *sc, uint16_t ofs) 31812e36acbSWarner Losh { 31912e36acbSWarner Losh return CSR_READ_2(sc, ofs + BWI_SPROM_START); 32012e36acbSWarner Losh } 32112e36acbSWarner Losh 32212e36acbSWarner Losh static __inline void 32312e36acbSWarner Losh bwi_setup_desc32(struct bwi_softc *sc, struct bwi_desc32 *desc_array, 32412e36acbSWarner Losh int ndesc, int desc_idx, bus_addr_t paddr, int buf_len, 32512e36acbSWarner Losh int tx) 32612e36acbSWarner Losh { 32712e36acbSWarner Losh struct bwi_desc32 *desc = &desc_array[desc_idx]; 32812e36acbSWarner Losh uint32_t ctrl, addr, addr_hi, addr_lo; 32912e36acbSWarner Losh 33012e36acbSWarner Losh addr_lo = __SHIFTOUT(paddr, BWI_DESC32_A_ADDR_MASK); 33112e36acbSWarner Losh addr_hi = __SHIFTOUT(paddr, BWI_DESC32_A_FUNC_MASK); 33212e36acbSWarner Losh 33312e36acbSWarner Losh addr = __SHIFTIN(addr_lo, BWI_DESC32_A_ADDR_MASK) | 33412e36acbSWarner Losh __SHIFTIN(BWI_DESC32_A_FUNC_TXRX, BWI_DESC32_A_FUNC_MASK); 33512e36acbSWarner Losh 33612e36acbSWarner Losh ctrl = __SHIFTIN(buf_len, BWI_DESC32_C_BUFLEN_MASK) | 33712e36acbSWarner Losh __SHIFTIN(addr_hi, BWI_DESC32_C_ADDRHI_MASK); 33812e36acbSWarner Losh if (desc_idx == ndesc - 1) 33912e36acbSWarner Losh ctrl |= BWI_DESC32_C_EOR; 34012e36acbSWarner Losh if (tx) { 34112e36acbSWarner Losh /* XXX */ 34212e36acbSWarner Losh ctrl |= BWI_DESC32_C_FRAME_START | 34312e36acbSWarner Losh BWI_DESC32_C_FRAME_END | 34412e36acbSWarner Losh BWI_DESC32_C_INTR; 34512e36acbSWarner Losh } 34612e36acbSWarner Losh 34712e36acbSWarner Losh desc->addr = htole32(addr); 34812e36acbSWarner Losh desc->ctrl = htole32(ctrl); 34912e36acbSWarner Losh } 35012e36acbSWarner Losh 35112e36acbSWarner Losh int 35212e36acbSWarner Losh bwi_attach(struct bwi_softc *sc) 35312e36acbSWarner Losh { 35412e36acbSWarner Losh struct ieee80211com *ic; 35512e36acbSWarner Losh device_t dev = sc->sc_dev; 35612e36acbSWarner Losh struct ifnet *ifp; 35712e36acbSWarner Losh struct bwi_mac *mac; 35812e36acbSWarner Losh struct bwi_phy *phy; 35912e36acbSWarner Losh int i, error; 36012e36acbSWarner Losh uint8_t bands; 36112e36acbSWarner Losh uint8_t macaddr[IEEE80211_ADDR_LEN]; 36212e36acbSWarner Losh 36312e36acbSWarner Losh BWI_LOCK_INIT(sc); 36412e36acbSWarner Losh 36512e36acbSWarner Losh /* 36612e36acbSWarner Losh * Initialize taskq and various tasks 36712e36acbSWarner Losh */ 36812e36acbSWarner Losh sc->sc_tq = taskqueue_create("bwi_taskq", M_NOWAIT | M_ZERO, 36912e36acbSWarner Losh taskqueue_thread_enqueue, &sc->sc_tq); 37012e36acbSWarner Losh taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", 37112e36acbSWarner Losh device_get_nameunit(dev)); 37212e36acbSWarner Losh TASK_INIT(&sc->sc_restart_task, 0, bwi_restart, sc); 37312e36acbSWarner Losh 37412e36acbSWarner Losh callout_init_mtx(&sc->sc_calib_ch, &sc->sc_mtx, 0); 37512e36acbSWarner Losh 37612e36acbSWarner Losh /* 37712e36acbSWarner Losh * Initialize sysctl variables 37812e36acbSWarner Losh */ 37912e36acbSWarner Losh sc->sc_fw_version = BWI_FW_VERSION3; 38012e36acbSWarner Losh sc->sc_led_idle = (2350 * hz) / 1000; 38112e36acbSWarner Losh sc->sc_led_blink = 1; 38212e36acbSWarner Losh sc->sc_txpwr_calib = 1; 38312e36acbSWarner Losh #ifdef BWI_DEBUG 38412e36acbSWarner Losh sc->sc_debug = bwi_debug; 38512e36acbSWarner Losh #endif 38612e36acbSWarner Losh bwi_power_on(sc, 1); 38712e36acbSWarner Losh 38812e36acbSWarner Losh error = bwi_bbp_attach(sc); 38912e36acbSWarner Losh if (error) 39012e36acbSWarner Losh goto fail; 39112e36acbSWarner Losh 39212e36acbSWarner Losh error = bwi_bbp_power_on(sc, BWI_CLOCK_MODE_FAST); 39312e36acbSWarner Losh if (error) 39412e36acbSWarner Losh goto fail; 39512e36acbSWarner Losh 39612e36acbSWarner Losh if (BWI_REGWIN_EXIST(&sc->sc_com_regwin)) { 39712e36acbSWarner Losh error = bwi_set_clock_delay(sc); 39812e36acbSWarner Losh if (error) 39912e36acbSWarner Losh goto fail; 40012e36acbSWarner Losh 40112e36acbSWarner Losh error = bwi_set_clock_mode(sc, BWI_CLOCK_MODE_FAST); 40212e36acbSWarner Losh if (error) 40312e36acbSWarner Losh goto fail; 40412e36acbSWarner Losh 40512e36acbSWarner Losh error = bwi_get_pwron_delay(sc); 40612e36acbSWarner Losh if (error) 40712e36acbSWarner Losh goto fail; 40812e36acbSWarner Losh } 40912e36acbSWarner Losh 41012e36acbSWarner Losh error = bwi_bus_attach(sc); 41112e36acbSWarner Losh if (error) 41212e36acbSWarner Losh goto fail; 41312e36acbSWarner Losh 41412e36acbSWarner Losh bwi_get_card_flags(sc); 41512e36acbSWarner Losh 41612e36acbSWarner Losh bwi_led_attach(sc); 41712e36acbSWarner Losh 41812e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) { 41912e36acbSWarner Losh struct bwi_regwin *old; 42012e36acbSWarner Losh 42112e36acbSWarner Losh mac = &sc->sc_mac[i]; 42212e36acbSWarner Losh error = bwi_regwin_switch(sc, &mac->mac_regwin, &old); 42312e36acbSWarner Losh if (error) 42412e36acbSWarner Losh goto fail; 42512e36acbSWarner Losh 42612e36acbSWarner Losh error = bwi_mac_lateattach(mac); 42712e36acbSWarner Losh if (error) 42812e36acbSWarner Losh goto fail; 42912e36acbSWarner Losh 43012e36acbSWarner Losh error = bwi_regwin_switch(sc, old, NULL); 43112e36acbSWarner Losh if (error) 43212e36acbSWarner Losh goto fail; 43312e36acbSWarner Losh } 43412e36acbSWarner Losh 43512e36acbSWarner Losh /* 43612e36acbSWarner Losh * XXX First MAC is known to exist 43712e36acbSWarner Losh * TODO2 43812e36acbSWarner Losh */ 43912e36acbSWarner Losh mac = &sc->sc_mac[0]; 44012e36acbSWarner Losh phy = &mac->mac_phy; 44112e36acbSWarner Losh 44212e36acbSWarner Losh bwi_bbp_power_off(sc); 44312e36acbSWarner Losh 44412e36acbSWarner Losh error = bwi_dma_alloc(sc); 44512e36acbSWarner Losh if (error) 44612e36acbSWarner Losh goto fail; 44712e36acbSWarner Losh 44812e36acbSWarner Losh ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 44912e36acbSWarner Losh if (ifp == NULL) { 45012e36acbSWarner Losh device_printf(dev, "can not if_alloc()\n"); 45112e36acbSWarner Losh error = ENOSPC; 45212e36acbSWarner Losh goto fail; 45312e36acbSWarner Losh } 45412e36acbSWarner Losh ic = ifp->if_l2com; 45512e36acbSWarner Losh 45612e36acbSWarner Losh /* set these up early for if_printf use */ 45712e36acbSWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 45812e36acbSWarner Losh 45912e36acbSWarner Losh ifp->if_softc = sc; 46012e36acbSWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 46112e36acbSWarner Losh ifp->if_init = bwi_init; 46212e36acbSWarner Losh ifp->if_ioctl = bwi_ioctl; 46312e36acbSWarner Losh ifp->if_start = bwi_start; 46412e36acbSWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 46512e36acbSWarner Losh ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 46612e36acbSWarner Losh IFQ_SET_READY(&ifp->if_snd); 46758fbe5abSJohn Baldwin callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0); 46812e36acbSWarner Losh 46912e36acbSWarner Losh /* 47012e36acbSWarner Losh * Setup ratesets, phytype, channels and get MAC address 47112e36acbSWarner Losh */ 47212e36acbSWarner Losh bands = 0; 47312e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11B || 47412e36acbSWarner Losh phy->phy_mode == IEEE80211_MODE_11G) { 47512e36acbSWarner Losh setbit(&bands, IEEE80211_MODE_11B); 47612e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11B) { 47712e36acbSWarner Losh ic->ic_phytype = IEEE80211_T_DS; 47812e36acbSWarner Losh } else { 47912e36acbSWarner Losh ic->ic_phytype = IEEE80211_T_OFDM; 48012e36acbSWarner Losh setbit(&bands, IEEE80211_MODE_11G); 48112e36acbSWarner Losh } 48212e36acbSWarner Losh 48312e36acbSWarner Losh bwi_get_eaddr(sc, BWI_SPROM_11BG_EADDR, macaddr); 48412e36acbSWarner Losh if (IEEE80211_IS_MULTICAST(macaddr)) { 48512e36acbSWarner Losh bwi_get_eaddr(sc, BWI_SPROM_11A_EADDR, macaddr); 48612e36acbSWarner Losh if (IEEE80211_IS_MULTICAST(macaddr)) { 48712e36acbSWarner Losh device_printf(dev, 48812e36acbSWarner Losh "invalid MAC address: %6D\n", 48912e36acbSWarner Losh macaddr, ":"); 49012e36acbSWarner Losh } 49112e36acbSWarner Losh } 49212e36acbSWarner Losh } else if (phy->phy_mode == IEEE80211_MODE_11A) { 49312e36acbSWarner Losh /* TODO:11A */ 49412e36acbSWarner Losh setbit(&bands, IEEE80211_MODE_11A); 49512e36acbSWarner Losh error = ENXIO; 49612e36acbSWarner Losh goto fail; 49712e36acbSWarner Losh } else { 49812e36acbSWarner Losh panic("unknown phymode %d\n", phy->phy_mode); 49912e36acbSWarner Losh } 50012e36acbSWarner Losh 50112e36acbSWarner Losh /* Get locale */ 50212e36acbSWarner Losh sc->sc_locale = __SHIFTOUT(bwi_read_sprom(sc, BWI_SPROM_CARD_INFO), 50312e36acbSWarner Losh BWI_SPROM_CARD_INFO_LOCALE); 50412e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "locale: %d\n", sc->sc_locale); 50512e36acbSWarner Losh /* XXX use locale */ 50612e36acbSWarner Losh ieee80211_init_channels(ic, NULL, &bands); 50712e36acbSWarner Losh 50812e36acbSWarner Losh ic->ic_ifp = ifp; 50912e36acbSWarner Losh ic->ic_caps = IEEE80211_C_STA | 51012e36acbSWarner Losh IEEE80211_C_SHSLOT | 51112e36acbSWarner Losh IEEE80211_C_SHPREAMBLE | 51212e36acbSWarner Losh IEEE80211_C_WPA | 51312e36acbSWarner Losh IEEE80211_C_BGSCAN | 51412e36acbSWarner Losh IEEE80211_C_MONITOR; 51512e36acbSWarner Losh ic->ic_opmode = IEEE80211_M_STA; 51612e36acbSWarner Losh ieee80211_ifattach(ic, macaddr); 51712e36acbSWarner Losh 51812e36acbSWarner Losh ic->ic_headroom = sizeof(struct bwi_txbuf_hdr); 51912e36acbSWarner Losh 52012e36acbSWarner Losh /* override default methods */ 52112e36acbSWarner Losh ic->ic_vap_create = bwi_vap_create; 52212e36acbSWarner Losh ic->ic_vap_delete = bwi_vap_delete; 52312e36acbSWarner Losh ic->ic_raw_xmit = bwi_raw_xmit; 52412e36acbSWarner Losh ic->ic_updateslot = bwi_updateslot; 52512e36acbSWarner Losh ic->ic_scan_start = bwi_scan_start; 52612e36acbSWarner Losh ic->ic_scan_end = bwi_scan_end; 52712e36acbSWarner Losh ic->ic_set_channel = bwi_set_channel; 52812e36acbSWarner Losh 52912e36acbSWarner Losh sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); 53012e36acbSWarner Losh 5315463c4a4SSam Leffler ieee80211_radiotap_attach(ic, 5325463c4a4SSam Leffler &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 5335463c4a4SSam Leffler BWI_TX_RADIOTAP_PRESENT, 5345463c4a4SSam Leffler &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 5355463c4a4SSam Leffler BWI_RX_RADIOTAP_PRESENT); 53612e36acbSWarner Losh 53712e36acbSWarner Losh /* 53812e36acbSWarner Losh * Add sysctl nodes 53912e36acbSWarner Losh */ 54012e36acbSWarner Losh SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 54112e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 54212e36acbSWarner Losh "fw_version", CTLFLAG_RD, &sc->sc_fw_version, 0, 54312e36acbSWarner Losh "Firmware version"); 54412e36acbSWarner Losh SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 54512e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 54612e36acbSWarner Losh "led_idle", CTLFLAG_RW, &sc->sc_led_idle, 0, 54712e36acbSWarner Losh "# ticks before LED enters idle state"); 54812e36acbSWarner Losh SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 54912e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 55012e36acbSWarner Losh "led_blink", CTLFLAG_RW, &sc->sc_led_blink, 0, 55112e36acbSWarner Losh "Allow LED to blink"); 55212e36acbSWarner Losh SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 55312e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 55412e36acbSWarner Losh "txpwr_calib", CTLFLAG_RW, &sc->sc_txpwr_calib, 0, 55512e36acbSWarner Losh "Enable software TX power calibration"); 55612e36acbSWarner Losh #ifdef BWI_DEBUG 55712e36acbSWarner Losh SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 55812e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 55912e36acbSWarner Losh "debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags"); 56012e36acbSWarner Losh #endif 56112e36acbSWarner Losh if (bootverbose) 56212e36acbSWarner Losh ieee80211_announce(ic); 56312e36acbSWarner Losh 56412e36acbSWarner Losh return (0); 56512e36acbSWarner Losh fail: 56612e36acbSWarner Losh BWI_LOCK_DESTROY(sc); 56712e36acbSWarner Losh return (error); 56812e36acbSWarner Losh } 56912e36acbSWarner Losh 57012e36acbSWarner Losh int 57112e36acbSWarner Losh bwi_detach(struct bwi_softc *sc) 57212e36acbSWarner Losh { 57312e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 57412e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 57512e36acbSWarner Losh int i; 57612e36acbSWarner Losh 57712e36acbSWarner Losh bwi_stop(sc, 1); 5787389f7dbSWarner Losh callout_drain(&sc->sc_led_blink_ch); 57912e36acbSWarner Losh callout_drain(&sc->sc_calib_ch); 58058fbe5abSJohn Baldwin callout_drain(&sc->sc_watchdog_timer); 58112e36acbSWarner Losh ieee80211_ifdetach(ic); 58212e36acbSWarner Losh 58312e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) 58412e36acbSWarner Losh bwi_mac_detach(&sc->sc_mac[i]); 58512e36acbSWarner Losh bwi_dma_free(sc); 58612e36acbSWarner Losh if_free(ifp); 58712e36acbSWarner Losh taskqueue_free(sc->sc_tq); 58812e36acbSWarner Losh 58912e36acbSWarner Losh BWI_LOCK_DESTROY(sc); 59012e36acbSWarner Losh 59112e36acbSWarner Losh return (0); 59212e36acbSWarner Losh } 59312e36acbSWarner Losh 59412e36acbSWarner Losh static struct ieee80211vap * 59512e36acbSWarner Losh bwi_vap_create(struct ieee80211com *ic, 59612e36acbSWarner Losh const char name[IFNAMSIZ], int unit, int opmode, int flags, 59712e36acbSWarner Losh const uint8_t bssid[IEEE80211_ADDR_LEN], 59812e36acbSWarner Losh const uint8_t mac[IEEE80211_ADDR_LEN]) 59912e36acbSWarner Losh { 60012e36acbSWarner Losh struct bwi_vap *bvp; 60112e36acbSWarner Losh struct ieee80211vap *vap; 60212e36acbSWarner Losh 60312e36acbSWarner Losh if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 60412e36acbSWarner Losh return NULL; 60512e36acbSWarner Losh bvp = (struct bwi_vap *) malloc(sizeof(struct bwi_vap), 60612e36acbSWarner Losh M_80211_VAP, M_WAITOK | M_ZERO); 60712e36acbSWarner Losh if (bvp == NULL) 60812e36acbSWarner Losh return NULL; 60912e36acbSWarner Losh vap = &bvp->bv_vap; 61012e36acbSWarner Losh /* enable s/w bmiss handling for sta mode */ 61112e36acbSWarner Losh ieee80211_vap_setup(ic, vap, name, unit, opmode, 61212e36acbSWarner Losh flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); 61312e36acbSWarner Losh 61412e36acbSWarner Losh /* override default methods */ 61512e36acbSWarner Losh bvp->bv_newstate = vap->iv_newstate; 61612e36acbSWarner Losh vap->iv_newstate = bwi_newstate; 61712e36acbSWarner Losh #if 0 61812e36acbSWarner Losh vap->iv_update_beacon = bwi_beacon_update; 61912e36acbSWarner Losh #endif 620b6108616SRui Paulo ieee80211_ratectl_init(vap); 62112e36acbSWarner Losh 62212e36acbSWarner Losh /* complete setup */ 62312e36acbSWarner Losh ieee80211_vap_attach(vap, bwi_media_change, ieee80211_media_status); 62412e36acbSWarner Losh ic->ic_opmode = opmode; 62512e36acbSWarner Losh return vap; 62612e36acbSWarner Losh } 62712e36acbSWarner Losh 62812e36acbSWarner Losh static void 62912e36acbSWarner Losh bwi_vap_delete(struct ieee80211vap *vap) 63012e36acbSWarner Losh { 63112e36acbSWarner Losh struct bwi_vap *bvp = BWI_VAP(vap); 63212e36acbSWarner Losh 633b6108616SRui Paulo ieee80211_ratectl_deinit(vap); 63412e36acbSWarner Losh ieee80211_vap_detach(vap); 63512e36acbSWarner Losh free(bvp, M_80211_VAP); 63612e36acbSWarner Losh } 63712e36acbSWarner Losh 63812e36acbSWarner Losh void 63912e36acbSWarner Losh bwi_suspend(struct bwi_softc *sc) 64012e36acbSWarner Losh { 64112e36acbSWarner Losh bwi_stop(sc, 1); 64212e36acbSWarner Losh } 64312e36acbSWarner Losh 64412e36acbSWarner Losh void 64512e36acbSWarner Losh bwi_resume(struct bwi_softc *sc) 64612e36acbSWarner Losh { 64712e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 64812e36acbSWarner Losh 64912e36acbSWarner Losh if (ifp->if_flags & IFF_UP) 65012e36acbSWarner Losh bwi_init(sc); 65112e36acbSWarner Losh } 65212e36acbSWarner Losh 65312e36acbSWarner Losh int 65412e36acbSWarner Losh bwi_shutdown(struct bwi_softc *sc) 65512e36acbSWarner Losh { 65612e36acbSWarner Losh bwi_stop(sc, 1); 65712e36acbSWarner Losh return 0; 65812e36acbSWarner Losh } 65912e36acbSWarner Losh 66012e36acbSWarner Losh static void 66112e36acbSWarner Losh bwi_power_on(struct bwi_softc *sc, int with_pll) 66212e36acbSWarner Losh { 66312e36acbSWarner Losh uint32_t gpio_in, gpio_out, gpio_en; 66412e36acbSWarner Losh uint16_t status; 66512e36acbSWarner Losh 66612e36acbSWarner Losh gpio_in = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_IN, 4); 66712e36acbSWarner Losh if (gpio_in & BWI_PCIM_GPIO_PWR_ON) 66812e36acbSWarner Losh goto back; 66912e36acbSWarner Losh 67012e36acbSWarner Losh gpio_out = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); 67112e36acbSWarner Losh gpio_en = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, 4); 67212e36acbSWarner Losh 67312e36acbSWarner Losh gpio_out |= BWI_PCIM_GPIO_PWR_ON; 67412e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PWR_ON; 67512e36acbSWarner Losh if (with_pll) { 67612e36acbSWarner Losh /* Turn off PLL first */ 67712e36acbSWarner Losh gpio_out |= BWI_PCIM_GPIO_PLL_PWR_OFF; 67812e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PLL_PWR_OFF; 67912e36acbSWarner Losh } 68012e36acbSWarner Losh 68112e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); 68212e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, gpio_en, 4); 68312e36acbSWarner Losh DELAY(1000); 68412e36acbSWarner Losh 68512e36acbSWarner Losh if (with_pll) { 68612e36acbSWarner Losh /* Turn on PLL */ 68712e36acbSWarner Losh gpio_out &= ~BWI_PCIM_GPIO_PLL_PWR_OFF; 68812e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); 68912e36acbSWarner Losh DELAY(5000); 69012e36acbSWarner Losh } 69112e36acbSWarner Losh 69212e36acbSWarner Losh back: 69312e36acbSWarner Losh /* Clear "Signaled Target Abort" */ 69412e36acbSWarner Losh status = pci_read_config(sc->sc_dev, PCIR_STATUS, 2); 69512e36acbSWarner Losh status &= ~PCIM_STATUS_STABORT; 69612e36acbSWarner Losh pci_write_config(sc->sc_dev, PCIR_STATUS, status, 2); 69712e36acbSWarner Losh } 69812e36acbSWarner Losh 69912e36acbSWarner Losh static int 70012e36acbSWarner Losh bwi_power_off(struct bwi_softc *sc, int with_pll) 70112e36acbSWarner Losh { 70212e36acbSWarner Losh uint32_t gpio_out, gpio_en; 70312e36acbSWarner Losh 70412e36acbSWarner Losh pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_IN, 4); /* dummy read */ 70512e36acbSWarner Losh gpio_out = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); 70612e36acbSWarner Losh gpio_en = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, 4); 70712e36acbSWarner Losh 70812e36acbSWarner Losh gpio_out &= ~BWI_PCIM_GPIO_PWR_ON; 70912e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PWR_ON; 71012e36acbSWarner Losh if (with_pll) { 71112e36acbSWarner Losh gpio_out |= BWI_PCIM_GPIO_PLL_PWR_OFF; 71212e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PLL_PWR_OFF; 71312e36acbSWarner Losh } 71412e36acbSWarner Losh 71512e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); 71612e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, gpio_en, 4); 71712e36acbSWarner Losh return 0; 71812e36acbSWarner Losh } 71912e36acbSWarner Losh 72012e36acbSWarner Losh int 72112e36acbSWarner Losh bwi_regwin_switch(struct bwi_softc *sc, struct bwi_regwin *rw, 72212e36acbSWarner Losh struct bwi_regwin **old_rw) 72312e36acbSWarner Losh { 72412e36acbSWarner Losh int error; 72512e36acbSWarner Losh 72612e36acbSWarner Losh if (old_rw != NULL) 72712e36acbSWarner Losh *old_rw = NULL; 72812e36acbSWarner Losh 72912e36acbSWarner Losh if (!BWI_REGWIN_EXIST(rw)) 73012e36acbSWarner Losh return EINVAL; 73112e36acbSWarner Losh 73212e36acbSWarner Losh if (sc->sc_cur_regwin != rw) { 73312e36acbSWarner Losh error = bwi_regwin_select(sc, rw->rw_id); 73412e36acbSWarner Losh if (error) { 73512e36acbSWarner Losh device_printf(sc->sc_dev, "can't select regwin %d\n", 73612e36acbSWarner Losh rw->rw_id); 73712e36acbSWarner Losh return error; 73812e36acbSWarner Losh } 73912e36acbSWarner Losh } 74012e36acbSWarner Losh 74112e36acbSWarner Losh if (old_rw != NULL) 74212e36acbSWarner Losh *old_rw = sc->sc_cur_regwin; 74312e36acbSWarner Losh sc->sc_cur_regwin = rw; 74412e36acbSWarner Losh return 0; 74512e36acbSWarner Losh } 74612e36acbSWarner Losh 74712e36acbSWarner Losh static int 74812e36acbSWarner Losh bwi_regwin_select(struct bwi_softc *sc, int id) 74912e36acbSWarner Losh { 75012e36acbSWarner Losh uint32_t win = BWI_PCIM_REGWIN(id); 75112e36acbSWarner Losh int i; 75212e36acbSWarner Losh 75312e36acbSWarner Losh #define RETRY_MAX 50 75412e36acbSWarner Losh for (i = 0; i < RETRY_MAX; ++i) { 75512e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_SEL_REGWIN, win, 4); 75612e36acbSWarner Losh if (pci_read_config(sc->sc_dev, BWI_PCIR_SEL_REGWIN, 4) == win) 75712e36acbSWarner Losh return 0; 75812e36acbSWarner Losh DELAY(10); 75912e36acbSWarner Losh } 76012e36acbSWarner Losh #undef RETRY_MAX 76112e36acbSWarner Losh 76212e36acbSWarner Losh return ENXIO; 76312e36acbSWarner Losh } 76412e36acbSWarner Losh 76512e36acbSWarner Losh static void 76612e36acbSWarner Losh bwi_regwin_info(struct bwi_softc *sc, uint16_t *type, uint8_t *rev) 76712e36acbSWarner Losh { 76812e36acbSWarner Losh uint32_t val; 76912e36acbSWarner Losh 77012e36acbSWarner Losh val = CSR_READ_4(sc, BWI_ID_HI); 77112e36acbSWarner Losh *type = BWI_ID_HI_REGWIN_TYPE(val); 77212e36acbSWarner Losh *rev = BWI_ID_HI_REGWIN_REV(val); 77312e36acbSWarner Losh 77412e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "regwin: type 0x%03x, rev %d, " 77512e36acbSWarner Losh "vendor 0x%04x\n", *type, *rev, 77612e36acbSWarner Losh __SHIFTOUT(val, BWI_ID_HI_REGWIN_VENDOR_MASK)); 77712e36acbSWarner Losh } 77812e36acbSWarner Losh 77912e36acbSWarner Losh static int 78012e36acbSWarner Losh bwi_bbp_attach(struct bwi_softc *sc) 78112e36acbSWarner Losh { 78212e36acbSWarner Losh #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 78312e36acbSWarner Losh uint16_t bbp_id, rw_type; 78412e36acbSWarner Losh uint8_t rw_rev; 78512e36acbSWarner Losh uint32_t info; 78612e36acbSWarner Losh int error, nregwin, i; 78712e36acbSWarner Losh 78812e36acbSWarner Losh /* 78912e36acbSWarner Losh * Get 0th regwin information 79012e36acbSWarner Losh * NOTE: 0th regwin should exist 79112e36acbSWarner Losh */ 79212e36acbSWarner Losh error = bwi_regwin_select(sc, 0); 79312e36acbSWarner Losh if (error) { 79412e36acbSWarner Losh device_printf(sc->sc_dev, "can't select regwin 0\n"); 79512e36acbSWarner Losh return error; 79612e36acbSWarner Losh } 79712e36acbSWarner Losh bwi_regwin_info(sc, &rw_type, &rw_rev); 79812e36acbSWarner Losh 79912e36acbSWarner Losh /* 80012e36acbSWarner Losh * Find out BBP id 80112e36acbSWarner Losh */ 80212e36acbSWarner Losh bbp_id = 0; 80312e36acbSWarner Losh info = 0; 80412e36acbSWarner Losh if (rw_type == BWI_REGWIN_T_COM) { 80512e36acbSWarner Losh info = CSR_READ_4(sc, BWI_INFO); 80612e36acbSWarner Losh bbp_id = __SHIFTOUT(info, BWI_INFO_BBPID_MASK); 80712e36acbSWarner Losh 80812e36acbSWarner Losh BWI_CREATE_REGWIN(&sc->sc_com_regwin, 0, rw_type, rw_rev); 80912e36acbSWarner Losh 81012e36acbSWarner Losh sc->sc_cap = CSR_READ_4(sc, BWI_CAPABILITY); 81112e36acbSWarner Losh } else { 81212e36acbSWarner Losh for (i = 0; i < N(bwi_bbpid_map); ++i) { 81312e36acbSWarner Losh if (sc->sc_pci_did >= bwi_bbpid_map[i].did_min && 81412e36acbSWarner Losh sc->sc_pci_did <= bwi_bbpid_map[i].did_max) { 81512e36acbSWarner Losh bbp_id = bwi_bbpid_map[i].bbp_id; 81612e36acbSWarner Losh break; 81712e36acbSWarner Losh } 81812e36acbSWarner Losh } 81912e36acbSWarner Losh if (bbp_id == 0) { 82012e36acbSWarner Losh device_printf(sc->sc_dev, "no BBP id for device id " 82112e36acbSWarner Losh "0x%04x\n", sc->sc_pci_did); 82212e36acbSWarner Losh return ENXIO; 82312e36acbSWarner Losh } 82412e36acbSWarner Losh 82512e36acbSWarner Losh info = __SHIFTIN(sc->sc_pci_revid, BWI_INFO_BBPREV_MASK) | 82612e36acbSWarner Losh __SHIFTIN(0, BWI_INFO_BBPPKG_MASK); 82712e36acbSWarner Losh } 82812e36acbSWarner Losh 82912e36acbSWarner Losh /* 83012e36acbSWarner Losh * Find out number of regwins 83112e36acbSWarner Losh */ 83212e36acbSWarner Losh nregwin = 0; 83312e36acbSWarner Losh if (rw_type == BWI_REGWIN_T_COM && rw_rev >= 4) { 83412e36acbSWarner Losh nregwin = __SHIFTOUT(info, BWI_INFO_NREGWIN_MASK); 83512e36acbSWarner Losh } else { 83612e36acbSWarner Losh for (i = 0; i < N(bwi_regwin_count); ++i) { 83712e36acbSWarner Losh if (bwi_regwin_count[i].bbp_id == bbp_id) { 83812e36acbSWarner Losh nregwin = bwi_regwin_count[i].nregwin; 83912e36acbSWarner Losh break; 84012e36acbSWarner Losh } 84112e36acbSWarner Losh } 84212e36acbSWarner Losh if (nregwin == 0) { 84312e36acbSWarner Losh device_printf(sc->sc_dev, "no number of win for " 84412e36acbSWarner Losh "BBP id 0x%04x\n", bbp_id); 84512e36acbSWarner Losh return ENXIO; 84612e36acbSWarner Losh } 84712e36acbSWarner Losh } 84812e36acbSWarner Losh 84912e36acbSWarner Losh /* Record BBP id/rev for later using */ 85012e36acbSWarner Losh sc->sc_bbp_id = bbp_id; 85112e36acbSWarner Losh sc->sc_bbp_rev = __SHIFTOUT(info, BWI_INFO_BBPREV_MASK); 85212e36acbSWarner Losh sc->sc_bbp_pkg = __SHIFTOUT(info, BWI_INFO_BBPPKG_MASK); 85312e36acbSWarner Losh device_printf(sc->sc_dev, "BBP: id 0x%04x, rev 0x%x, pkg %d\n", 85412e36acbSWarner Losh sc->sc_bbp_id, sc->sc_bbp_rev, sc->sc_bbp_pkg); 85512e36acbSWarner Losh 85612e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "nregwin %d, cap 0x%08x\n", 85712e36acbSWarner Losh nregwin, sc->sc_cap); 85812e36acbSWarner Losh 85912e36acbSWarner Losh /* 86012e36acbSWarner Losh * Create rest of the regwins 86112e36acbSWarner Losh */ 86212e36acbSWarner Losh 86312e36acbSWarner Losh /* Don't re-create common regwin, if it is already created */ 86412e36acbSWarner Losh i = BWI_REGWIN_EXIST(&sc->sc_com_regwin) ? 1 : 0; 86512e36acbSWarner Losh 86612e36acbSWarner Losh for (; i < nregwin; ++i) { 86712e36acbSWarner Losh /* 86812e36acbSWarner Losh * Get regwin information 86912e36acbSWarner Losh */ 87012e36acbSWarner Losh error = bwi_regwin_select(sc, i); 87112e36acbSWarner Losh if (error) { 87212e36acbSWarner Losh device_printf(sc->sc_dev, 87312e36acbSWarner Losh "can't select regwin %d\n", i); 87412e36acbSWarner Losh return error; 87512e36acbSWarner Losh } 87612e36acbSWarner Losh bwi_regwin_info(sc, &rw_type, &rw_rev); 87712e36acbSWarner Losh 87812e36acbSWarner Losh /* 87912e36acbSWarner Losh * Try attach: 88012e36acbSWarner Losh * 1) Bus (PCI/PCIE) regwin 88112e36acbSWarner Losh * 2) MAC regwin 88212e36acbSWarner Losh * Ignore rest types of regwin 88312e36acbSWarner Losh */ 88412e36acbSWarner Losh if (rw_type == BWI_REGWIN_T_BUSPCI || 88512e36acbSWarner Losh rw_type == BWI_REGWIN_T_BUSPCIE) { 88612e36acbSWarner Losh if (BWI_REGWIN_EXIST(&sc->sc_bus_regwin)) { 88712e36acbSWarner Losh device_printf(sc->sc_dev, 88812e36acbSWarner Losh "bus regwin already exists\n"); 88912e36acbSWarner Losh } else { 89012e36acbSWarner Losh BWI_CREATE_REGWIN(&sc->sc_bus_regwin, i, 89112e36acbSWarner Losh rw_type, rw_rev); 89212e36acbSWarner Losh } 89312e36acbSWarner Losh } else if (rw_type == BWI_REGWIN_T_MAC) { 89412e36acbSWarner Losh /* XXX ignore return value */ 89512e36acbSWarner Losh bwi_mac_attach(sc, i, rw_rev); 89612e36acbSWarner Losh } 89712e36acbSWarner Losh } 89812e36acbSWarner Losh 89912e36acbSWarner Losh /* At least one MAC shold exist */ 90012e36acbSWarner Losh if (!BWI_REGWIN_EXIST(&sc->sc_mac[0].mac_regwin)) { 90112e36acbSWarner Losh device_printf(sc->sc_dev, "no MAC was found\n"); 90212e36acbSWarner Losh return ENXIO; 90312e36acbSWarner Losh } 90412e36acbSWarner Losh KASSERT(sc->sc_nmac > 0, ("no mac's")); 90512e36acbSWarner Losh 90612e36acbSWarner Losh /* Bus regwin must exist */ 90712e36acbSWarner Losh if (!BWI_REGWIN_EXIST(&sc->sc_bus_regwin)) { 90812e36acbSWarner Losh device_printf(sc->sc_dev, "no bus regwin was found\n"); 90912e36acbSWarner Losh return ENXIO; 91012e36acbSWarner Losh } 91112e36acbSWarner Losh 91212e36acbSWarner Losh /* Start with first MAC */ 91312e36acbSWarner Losh error = bwi_regwin_switch(sc, &sc->sc_mac[0].mac_regwin, NULL); 91412e36acbSWarner Losh if (error) 91512e36acbSWarner Losh return error; 91612e36acbSWarner Losh 91712e36acbSWarner Losh return 0; 91812e36acbSWarner Losh #undef N 91912e36acbSWarner Losh } 92012e36acbSWarner Losh 92112e36acbSWarner Losh int 92212e36acbSWarner Losh bwi_bus_init(struct bwi_softc *sc, struct bwi_mac *mac) 92312e36acbSWarner Losh { 92412e36acbSWarner Losh struct bwi_regwin *old, *bus; 92512e36acbSWarner Losh uint32_t val; 92612e36acbSWarner Losh int error; 92712e36acbSWarner Losh 92812e36acbSWarner Losh bus = &sc->sc_bus_regwin; 92912e36acbSWarner Losh KASSERT(sc->sc_cur_regwin == &mac->mac_regwin, ("not cur regwin")); 93012e36acbSWarner Losh 93112e36acbSWarner Losh /* 93212e36acbSWarner Losh * Tell bus to generate requested interrupts 93312e36acbSWarner Losh */ 93412e36acbSWarner Losh if (bus->rw_rev < 6 && bus->rw_type == BWI_REGWIN_T_BUSPCI) { 93512e36acbSWarner Losh /* 93612e36acbSWarner Losh * NOTE: Read BWI_FLAGS from MAC regwin 93712e36acbSWarner Losh */ 93812e36acbSWarner Losh val = CSR_READ_4(sc, BWI_FLAGS); 93912e36acbSWarner Losh 94012e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, &old); 94112e36acbSWarner Losh if (error) 94212e36acbSWarner Losh return error; 94312e36acbSWarner Losh 94412e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_INTRVEC, (val & BWI_FLAGS_INTR_MASK)); 94512e36acbSWarner Losh } else { 94612e36acbSWarner Losh uint32_t mac_mask; 94712e36acbSWarner Losh 94812e36acbSWarner Losh mac_mask = 1 << mac->mac_id; 94912e36acbSWarner Losh 95012e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, &old); 95112e36acbSWarner Losh if (error) 95212e36acbSWarner Losh return error; 95312e36acbSWarner Losh 95412e36acbSWarner Losh val = pci_read_config(sc->sc_dev, BWI_PCIR_INTCTL, 4); 95512e36acbSWarner Losh val |= mac_mask << 8; 95612e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_INTCTL, val, 4); 95712e36acbSWarner Losh } 95812e36acbSWarner Losh 95912e36acbSWarner Losh if (sc->sc_flags & BWI_F_BUS_INITED) 96012e36acbSWarner Losh goto back; 96112e36acbSWarner Losh 96212e36acbSWarner Losh if (bus->rw_type == BWI_REGWIN_T_BUSPCI) { 96312e36acbSWarner Losh /* 96412e36acbSWarner Losh * Enable prefetch and burst 96512e36acbSWarner Losh */ 96612e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_BUS_CONFIG, 96712e36acbSWarner Losh BWI_BUS_CONFIG_PREFETCH | BWI_BUS_CONFIG_BURST); 96812e36acbSWarner Losh 96912e36acbSWarner Losh if (bus->rw_rev < 5) { 97012e36acbSWarner Losh struct bwi_regwin *com = &sc->sc_com_regwin; 97112e36acbSWarner Losh 97212e36acbSWarner Losh /* 97312e36acbSWarner Losh * Configure timeouts for bus operation 97412e36acbSWarner Losh */ 97512e36acbSWarner Losh 97612e36acbSWarner Losh /* 97712e36acbSWarner Losh * Set service timeout and request timeout 97812e36acbSWarner Losh */ 97912e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_CONF_LO, 98012e36acbSWarner Losh __SHIFTIN(BWI_CONF_LO_SERVTO, BWI_CONF_LO_SERVTO_MASK) | 98112e36acbSWarner Losh __SHIFTIN(BWI_CONF_LO_REQTO, BWI_CONF_LO_REQTO_MASK)); 98212e36acbSWarner Losh 98312e36acbSWarner Losh /* 98412e36acbSWarner Losh * If there is common regwin, we switch to that regwin 98512e36acbSWarner Losh * and switch back to bus regwin once we have done. 98612e36acbSWarner Losh */ 98712e36acbSWarner Losh if (BWI_REGWIN_EXIST(com)) { 98812e36acbSWarner Losh error = bwi_regwin_switch(sc, com, NULL); 98912e36acbSWarner Losh if (error) 99012e36acbSWarner Losh return error; 99112e36acbSWarner Losh } 99212e36acbSWarner Losh 99312e36acbSWarner Losh /* Let bus know what we have changed */ 99412e36acbSWarner Losh CSR_WRITE_4(sc, BWI_BUS_ADDR, BWI_BUS_ADDR_MAGIC); 99512e36acbSWarner Losh CSR_READ_4(sc, BWI_BUS_ADDR); /* Flush */ 99612e36acbSWarner Losh CSR_WRITE_4(sc, BWI_BUS_DATA, 0); 99712e36acbSWarner Losh CSR_READ_4(sc, BWI_BUS_DATA); /* Flush */ 99812e36acbSWarner Losh 99912e36acbSWarner Losh if (BWI_REGWIN_EXIST(com)) { 100012e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, NULL); 100112e36acbSWarner Losh if (error) 100212e36acbSWarner Losh return error; 100312e36acbSWarner Losh } 100412e36acbSWarner Losh } else if (bus->rw_rev >= 11) { 100512e36acbSWarner Losh /* 100612e36acbSWarner Losh * Enable memory read multiple 100712e36acbSWarner Losh */ 100812e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_BUS_CONFIG, BWI_BUS_CONFIG_MRM); 100912e36acbSWarner Losh } 101012e36acbSWarner Losh } else { 101112e36acbSWarner Losh /* TODO:PCIE */ 101212e36acbSWarner Losh } 101312e36acbSWarner Losh 101412e36acbSWarner Losh sc->sc_flags |= BWI_F_BUS_INITED; 101512e36acbSWarner Losh back: 101612e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 101712e36acbSWarner Losh } 101812e36acbSWarner Losh 101912e36acbSWarner Losh static void 102012e36acbSWarner Losh bwi_get_card_flags(struct bwi_softc *sc) 102112e36acbSWarner Losh { 102212e36acbSWarner Losh #define PCI_VENDOR_APPLE 0x106b 102312e36acbSWarner Losh #define PCI_VENDOR_DELL 0x1028 102412e36acbSWarner Losh sc->sc_card_flags = bwi_read_sprom(sc, BWI_SPROM_CARD_FLAGS); 102512e36acbSWarner Losh if (sc->sc_card_flags == 0xffff) 102612e36acbSWarner Losh sc->sc_card_flags = 0; 102712e36acbSWarner Losh 102812e36acbSWarner Losh if (sc->sc_pci_subvid == PCI_VENDOR_DELL && 102912e36acbSWarner Losh sc->sc_bbp_id == BWI_BBPID_BCM4301 && 103012e36acbSWarner Losh sc->sc_pci_revid == 0x74) 103112e36acbSWarner Losh sc->sc_card_flags |= BWI_CARD_F_BT_COEXIST; 103212e36acbSWarner Losh 103312e36acbSWarner Losh if (sc->sc_pci_subvid == PCI_VENDOR_APPLE && 103412e36acbSWarner Losh sc->sc_pci_subdid == 0x4e && /* XXX */ 103512e36acbSWarner Losh sc->sc_pci_revid > 0x40) 103612e36acbSWarner Losh sc->sc_card_flags |= BWI_CARD_F_PA_GPIO9; 103712e36acbSWarner Losh 103812e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "card flags 0x%04x\n", sc->sc_card_flags); 103912e36acbSWarner Losh #undef PCI_VENDOR_DELL 104012e36acbSWarner Losh #undef PCI_VENDOR_APPLE 104112e36acbSWarner Losh } 104212e36acbSWarner Losh 104312e36acbSWarner Losh static void 104412e36acbSWarner Losh bwi_get_eaddr(struct bwi_softc *sc, uint16_t eaddr_ofs, uint8_t *eaddr) 104512e36acbSWarner Losh { 104612e36acbSWarner Losh int i; 104712e36acbSWarner Losh 104812e36acbSWarner Losh for (i = 0; i < 3; ++i) { 104912e36acbSWarner Losh *((uint16_t *)eaddr + i) = 105012e36acbSWarner Losh htobe16(bwi_read_sprom(sc, eaddr_ofs + 2 * i)); 105112e36acbSWarner Losh } 105212e36acbSWarner Losh } 105312e36acbSWarner Losh 105412e36acbSWarner Losh static void 105512e36acbSWarner Losh bwi_get_clock_freq(struct bwi_softc *sc, struct bwi_clock_freq *freq) 105612e36acbSWarner Losh { 105712e36acbSWarner Losh struct bwi_regwin *com; 105812e36acbSWarner Losh uint32_t val; 105912e36acbSWarner Losh u_int div; 106012e36acbSWarner Losh int src; 106112e36acbSWarner Losh 106212e36acbSWarner Losh bzero(freq, sizeof(*freq)); 106312e36acbSWarner Losh com = &sc->sc_com_regwin; 106412e36acbSWarner Losh 106512e36acbSWarner Losh KASSERT(BWI_REGWIN_EXIST(com), ("regwin does not exist")); 106612e36acbSWarner Losh KASSERT(sc->sc_cur_regwin == com, ("wrong regwin")); 106712e36acbSWarner Losh KASSERT(sc->sc_cap & BWI_CAP_CLKMODE, ("wrong clock mode")); 106812e36acbSWarner Losh 106912e36acbSWarner Losh /* 107012e36acbSWarner Losh * Calculate clock frequency 107112e36acbSWarner Losh */ 107212e36acbSWarner Losh src = -1; 107312e36acbSWarner Losh div = 0; 107412e36acbSWarner Losh if (com->rw_rev < 6) { 107512e36acbSWarner Losh val = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); 107612e36acbSWarner Losh if (val & BWI_PCIM_GPIO_OUT_CLKSRC) { 107712e36acbSWarner Losh src = BWI_CLKSRC_PCI; 107812e36acbSWarner Losh div = 64; 107912e36acbSWarner Losh } else { 108012e36acbSWarner Losh src = BWI_CLKSRC_CS_OSC; 108112e36acbSWarner Losh div = 32; 108212e36acbSWarner Losh } 108312e36acbSWarner Losh } else if (com->rw_rev < 10) { 108412e36acbSWarner Losh val = CSR_READ_4(sc, BWI_CLOCK_CTRL); 108512e36acbSWarner Losh 108612e36acbSWarner Losh src = __SHIFTOUT(val, BWI_CLOCK_CTRL_CLKSRC); 108712e36acbSWarner Losh if (src == BWI_CLKSRC_LP_OSC) { 108812e36acbSWarner Losh div = 1; 108912e36acbSWarner Losh } else { 109012e36acbSWarner Losh div = (__SHIFTOUT(val, BWI_CLOCK_CTRL_FDIV) + 1) << 2; 109112e36acbSWarner Losh 109212e36acbSWarner Losh /* Unknown source */ 109312e36acbSWarner Losh if (src >= BWI_CLKSRC_MAX) 109412e36acbSWarner Losh src = BWI_CLKSRC_CS_OSC; 109512e36acbSWarner Losh } 109612e36acbSWarner Losh } else { 109712e36acbSWarner Losh val = CSR_READ_4(sc, BWI_CLOCK_INFO); 109812e36acbSWarner Losh 109912e36acbSWarner Losh src = BWI_CLKSRC_CS_OSC; 110012e36acbSWarner Losh div = (__SHIFTOUT(val, BWI_CLOCK_INFO_FDIV) + 1) << 2; 110112e36acbSWarner Losh } 110212e36acbSWarner Losh 110312e36acbSWarner Losh KASSERT(src >= 0 && src < BWI_CLKSRC_MAX, ("bad src %d", src)); 110412e36acbSWarner Losh KASSERT(div != 0, ("div zero")); 110512e36acbSWarner Losh 110612e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "clksrc %s\n", 110712e36acbSWarner Losh src == BWI_CLKSRC_PCI ? "PCI" : 110812e36acbSWarner Losh (src == BWI_CLKSRC_LP_OSC ? "LP_OSC" : "CS_OSC")); 110912e36acbSWarner Losh 111012e36acbSWarner Losh freq->clkfreq_min = bwi_clkfreq[src].freq_min / div; 111112e36acbSWarner Losh freq->clkfreq_max = bwi_clkfreq[src].freq_max / div; 111212e36acbSWarner Losh 111312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "clkfreq min %u, max %u\n", 111412e36acbSWarner Losh freq->clkfreq_min, freq->clkfreq_max); 111512e36acbSWarner Losh } 111612e36acbSWarner Losh 111712e36acbSWarner Losh static int 111812e36acbSWarner Losh bwi_set_clock_mode(struct bwi_softc *sc, enum bwi_clock_mode clk_mode) 111912e36acbSWarner Losh { 112012e36acbSWarner Losh struct bwi_regwin *old, *com; 112112e36acbSWarner Losh uint32_t clk_ctrl, clk_src; 112212e36acbSWarner Losh int error, pwr_off = 0; 112312e36acbSWarner Losh 112412e36acbSWarner Losh com = &sc->sc_com_regwin; 112512e36acbSWarner Losh if (!BWI_REGWIN_EXIST(com)) 112612e36acbSWarner Losh return 0; 112712e36acbSWarner Losh 112812e36acbSWarner Losh if (com->rw_rev >= 10 || com->rw_rev < 6) 112912e36acbSWarner Losh return 0; 113012e36acbSWarner Losh 113112e36acbSWarner Losh /* 113212e36acbSWarner Losh * For common regwin whose rev is [6, 10), the chip 113312e36acbSWarner Losh * must be capable to change clock mode. 113412e36acbSWarner Losh */ 113512e36acbSWarner Losh if ((sc->sc_cap & BWI_CAP_CLKMODE) == 0) 113612e36acbSWarner Losh return 0; 113712e36acbSWarner Losh 113812e36acbSWarner Losh error = bwi_regwin_switch(sc, com, &old); 113912e36acbSWarner Losh if (error) 114012e36acbSWarner Losh return error; 114112e36acbSWarner Losh 114212e36acbSWarner Losh if (clk_mode == BWI_CLOCK_MODE_FAST) 114312e36acbSWarner Losh bwi_power_on(sc, 0); /* Don't turn on PLL */ 114412e36acbSWarner Losh 114512e36acbSWarner Losh clk_ctrl = CSR_READ_4(sc, BWI_CLOCK_CTRL); 114612e36acbSWarner Losh clk_src = __SHIFTOUT(clk_ctrl, BWI_CLOCK_CTRL_CLKSRC); 114712e36acbSWarner Losh 114812e36acbSWarner Losh switch (clk_mode) { 114912e36acbSWarner Losh case BWI_CLOCK_MODE_FAST: 115012e36acbSWarner Losh clk_ctrl &= ~BWI_CLOCK_CTRL_SLOW; 115112e36acbSWarner Losh clk_ctrl |= BWI_CLOCK_CTRL_IGNPLL; 115212e36acbSWarner Losh break; 115312e36acbSWarner Losh case BWI_CLOCK_MODE_SLOW: 115412e36acbSWarner Losh clk_ctrl |= BWI_CLOCK_CTRL_SLOW; 115512e36acbSWarner Losh break; 115612e36acbSWarner Losh case BWI_CLOCK_MODE_DYN: 115712e36acbSWarner Losh clk_ctrl &= ~(BWI_CLOCK_CTRL_SLOW | 115812e36acbSWarner Losh BWI_CLOCK_CTRL_IGNPLL | 115912e36acbSWarner Losh BWI_CLOCK_CTRL_NODYN); 116012e36acbSWarner Losh if (clk_src != BWI_CLKSRC_CS_OSC) { 116112e36acbSWarner Losh clk_ctrl |= BWI_CLOCK_CTRL_NODYN; 116212e36acbSWarner Losh pwr_off = 1; 116312e36acbSWarner Losh } 116412e36acbSWarner Losh break; 116512e36acbSWarner Losh } 116612e36acbSWarner Losh CSR_WRITE_4(sc, BWI_CLOCK_CTRL, clk_ctrl); 116712e36acbSWarner Losh 116812e36acbSWarner Losh if (pwr_off) 116912e36acbSWarner Losh bwi_power_off(sc, 0); /* Leave PLL as it is */ 117012e36acbSWarner Losh 117112e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 117212e36acbSWarner Losh } 117312e36acbSWarner Losh 117412e36acbSWarner Losh static int 117512e36acbSWarner Losh bwi_set_clock_delay(struct bwi_softc *sc) 117612e36acbSWarner Losh { 117712e36acbSWarner Losh struct bwi_regwin *old, *com; 117812e36acbSWarner Losh int error; 117912e36acbSWarner Losh 118012e36acbSWarner Losh com = &sc->sc_com_regwin; 118112e36acbSWarner Losh if (!BWI_REGWIN_EXIST(com)) 118212e36acbSWarner Losh return 0; 118312e36acbSWarner Losh 118412e36acbSWarner Losh error = bwi_regwin_switch(sc, com, &old); 118512e36acbSWarner Losh if (error) 118612e36acbSWarner Losh return error; 118712e36acbSWarner Losh 118812e36acbSWarner Losh if (sc->sc_bbp_id == BWI_BBPID_BCM4321) { 118912e36acbSWarner Losh if (sc->sc_bbp_rev == 0) 119012e36acbSWarner Losh CSR_WRITE_4(sc, BWI_CONTROL, BWI_CONTROL_MAGIC0); 119112e36acbSWarner Losh else if (sc->sc_bbp_rev == 1) 119212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_CONTROL, BWI_CONTROL_MAGIC1); 119312e36acbSWarner Losh } 119412e36acbSWarner Losh 119512e36acbSWarner Losh if (sc->sc_cap & BWI_CAP_CLKMODE) { 119612e36acbSWarner Losh if (com->rw_rev >= 10) { 119712e36acbSWarner Losh CSR_FILT_SETBITS_4(sc, BWI_CLOCK_INFO, 0xffff, 0x40000); 119812e36acbSWarner Losh } else { 119912e36acbSWarner Losh struct bwi_clock_freq freq; 120012e36acbSWarner Losh 120112e36acbSWarner Losh bwi_get_clock_freq(sc, &freq); 120212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_PLL_ON_DELAY, 120312e36acbSWarner Losh howmany(freq.clkfreq_max * 150, 1000000)); 120412e36acbSWarner Losh CSR_WRITE_4(sc, BWI_FREQ_SEL_DELAY, 120512e36acbSWarner Losh howmany(freq.clkfreq_max * 15, 1000000)); 120612e36acbSWarner Losh } 120712e36acbSWarner Losh } 120812e36acbSWarner Losh 120912e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 121012e36acbSWarner Losh } 121112e36acbSWarner Losh 121212e36acbSWarner Losh static void 121312e36acbSWarner Losh bwi_init(void *xsc) 121412e36acbSWarner Losh { 121512e36acbSWarner Losh struct bwi_softc *sc = xsc; 121612e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 121712e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 121812e36acbSWarner Losh 121912e36acbSWarner Losh BWI_LOCK(sc); 122012e36acbSWarner Losh bwi_init_statechg(sc, 1); 122112e36acbSWarner Losh BWI_UNLOCK(sc); 122212e36acbSWarner Losh 122312e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 122412e36acbSWarner Losh ieee80211_start_all(ic); /* start all vap's */ 122512e36acbSWarner Losh } 122612e36acbSWarner Losh 122712e36acbSWarner Losh static void 122812e36acbSWarner Losh bwi_init_statechg(struct bwi_softc *sc, int statechg) 122912e36acbSWarner Losh { 123012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 123112e36acbSWarner Losh struct bwi_mac *mac; 123212e36acbSWarner Losh int error; 123312e36acbSWarner Losh 123412e36acbSWarner Losh bwi_stop_locked(sc, statechg); 123512e36acbSWarner Losh 123612e36acbSWarner Losh bwi_bbp_power_on(sc, BWI_CLOCK_MODE_FAST); 123712e36acbSWarner Losh 123812e36acbSWarner Losh /* TODO: 2 MAC */ 123912e36acbSWarner Losh 124012e36acbSWarner Losh mac = &sc->sc_mac[0]; 124112e36acbSWarner Losh error = bwi_regwin_switch(sc, &mac->mac_regwin, NULL); 124212e36acbSWarner Losh if (error) { 124312e36acbSWarner Losh if_printf(ifp, "%s: error %d on regwin switch\n", 124412e36acbSWarner Losh __func__, error); 124512e36acbSWarner Losh goto bad; 124612e36acbSWarner Losh } 124712e36acbSWarner Losh error = bwi_mac_init(mac); 124812e36acbSWarner Losh if (error) { 124912e36acbSWarner Losh if_printf(ifp, "%s: error %d on MAC init\n", __func__, error); 125012e36acbSWarner Losh goto bad; 125112e36acbSWarner Losh } 125212e36acbSWarner Losh 125312e36acbSWarner Losh bwi_bbp_power_on(sc, BWI_CLOCK_MODE_DYN); 125412e36acbSWarner Losh 125512e36acbSWarner Losh bwi_set_bssid(sc, bwi_zero_addr); /* Clear BSSID */ 125612e36acbSWarner Losh bwi_set_addr_filter(sc, BWI_ADDR_FILTER_MYADDR, IF_LLADDR(ifp)); 125712e36acbSWarner Losh 125812e36acbSWarner Losh bwi_mac_reset_hwkeys(mac); 125912e36acbSWarner Losh 126012e36acbSWarner Losh if ((mac->mac_flags & BWI_MAC_F_HAS_TXSTATS) == 0) { 126112e36acbSWarner Losh int i; 126212e36acbSWarner Losh 126312e36acbSWarner Losh #define NRETRY 1000 126412e36acbSWarner Losh /* 126512e36acbSWarner Losh * Drain any possible pending TX status 126612e36acbSWarner Losh */ 126712e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 126812e36acbSWarner Losh if ((CSR_READ_4(sc, BWI_TXSTATUS0) & 126912e36acbSWarner Losh BWI_TXSTATUS0_VALID) == 0) 127012e36acbSWarner Losh break; 127112e36acbSWarner Losh CSR_READ_4(sc, BWI_TXSTATUS1); 127212e36acbSWarner Losh } 127312e36acbSWarner Losh if (i == NRETRY) 127412e36acbSWarner Losh if_printf(ifp, "%s: can't drain TX status\n", __func__); 127512e36acbSWarner Losh #undef NRETRY 127612e36acbSWarner Losh } 127712e36acbSWarner Losh 127812e36acbSWarner Losh if (mac->mac_phy.phy_mode == IEEE80211_MODE_11G) 127912e36acbSWarner Losh bwi_mac_updateslot(mac, 1); 128012e36acbSWarner Losh 128112e36acbSWarner Losh /* Start MAC */ 128212e36acbSWarner Losh error = bwi_mac_start(mac); 128312e36acbSWarner Losh if (error) { 128412e36acbSWarner Losh if_printf(ifp, "%s: error %d starting MAC\n", __func__, error); 128512e36acbSWarner Losh goto bad; 128612e36acbSWarner Losh } 128712e36acbSWarner Losh 128812e36acbSWarner Losh /* Clear stop flag before enabling interrupt */ 128912e36acbSWarner Losh sc->sc_flags &= ~BWI_F_STOP; 129012e36acbSWarner Losh 129112e36acbSWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 129258fbe5abSJohn Baldwin callout_reset(&sc->sc_watchdog_timer, hz, bwi_watchdog, sc); 129312e36acbSWarner Losh 129412e36acbSWarner Losh /* Enable intrs */ 129512e36acbSWarner Losh bwi_enable_intrs(sc, BWI_INIT_INTRS); 129612e36acbSWarner Losh return; 129712e36acbSWarner Losh bad: 129812e36acbSWarner Losh bwi_stop_locked(sc, 1); 129912e36acbSWarner Losh } 130012e36acbSWarner Losh 130112e36acbSWarner Losh static int 130212e36acbSWarner Losh bwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 130312e36acbSWarner Losh { 130412e36acbSWarner Losh #define IS_RUNNING(ifp) \ 130512e36acbSWarner Losh ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) 130612e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 130712e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 130812e36acbSWarner Losh struct ifreq *ifr = (struct ifreq *) data; 130912e36acbSWarner Losh int error = 0, startall = 0; 131012e36acbSWarner Losh 131112e36acbSWarner Losh switch (cmd) { 131212e36acbSWarner Losh case SIOCSIFFLAGS: 131312e36acbSWarner Losh BWI_LOCK(sc); 131412e36acbSWarner Losh if (IS_RUNNING(ifp)) { 131512e36acbSWarner Losh struct bwi_mac *mac; 131612e36acbSWarner Losh int promisc = -1; 131712e36acbSWarner Losh 131812e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 131912e36acbSWarner Losh ("current regwin type %d", 132012e36acbSWarner Losh sc->sc_cur_regwin->rw_type)); 132112e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 132212e36acbSWarner Losh 132312e36acbSWarner Losh if ((ifp->if_flags & IFF_PROMISC) && 132412e36acbSWarner Losh (sc->sc_flags & BWI_F_PROMISC) == 0) { 132512e36acbSWarner Losh promisc = 1; 132612e36acbSWarner Losh sc->sc_flags |= BWI_F_PROMISC; 132712e36acbSWarner Losh } else if ((ifp->if_flags & IFF_PROMISC) == 0 && 132812e36acbSWarner Losh (sc->sc_flags & BWI_F_PROMISC)) { 132912e36acbSWarner Losh promisc = 0; 133012e36acbSWarner Losh sc->sc_flags &= ~BWI_F_PROMISC; 133112e36acbSWarner Losh } 133212e36acbSWarner Losh 133312e36acbSWarner Losh if (promisc >= 0) 133412e36acbSWarner Losh bwi_mac_set_promisc(mac, promisc); 133512e36acbSWarner Losh } 133612e36acbSWarner Losh 133712e36acbSWarner Losh if (ifp->if_flags & IFF_UP) { 133812e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 133912e36acbSWarner Losh bwi_init_statechg(sc, 1); 134012e36acbSWarner Losh startall = 1; 134112e36acbSWarner Losh } 134212e36acbSWarner Losh } else { 134312e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 134412e36acbSWarner Losh bwi_stop_locked(sc, 1); 134512e36acbSWarner Losh } 134612e36acbSWarner Losh BWI_UNLOCK(sc); 134712e36acbSWarner Losh if (startall) 134812e36acbSWarner Losh ieee80211_start_all(ic); 134912e36acbSWarner Losh break; 135012e36acbSWarner Losh case SIOCGIFMEDIA: 135112e36acbSWarner Losh error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 135212e36acbSWarner Losh break; 135312e36acbSWarner Losh case SIOCGIFADDR: 135412e36acbSWarner Losh error = ether_ioctl(ifp, cmd, data); 135512e36acbSWarner Losh break; 135612e36acbSWarner Losh default: 135712e36acbSWarner Losh error = EINVAL; 135812e36acbSWarner Losh break; 135912e36acbSWarner Losh } 136012e36acbSWarner Losh return error; 136112e36acbSWarner Losh #undef IS_RUNNING 136212e36acbSWarner Losh } 136312e36acbSWarner Losh 136412e36acbSWarner Losh static void 136512e36acbSWarner Losh bwi_start(struct ifnet *ifp) 136612e36acbSWarner Losh { 136712e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 136812e36acbSWarner Losh 136912e36acbSWarner Losh BWI_LOCK(sc); 137012e36acbSWarner Losh bwi_start_locked(ifp); 137112e36acbSWarner Losh BWI_UNLOCK(sc); 137212e36acbSWarner Losh } 137312e36acbSWarner Losh 137412e36acbSWarner Losh static void 137512e36acbSWarner Losh bwi_start_locked(struct ifnet *ifp) 137612e36acbSWarner Losh { 137712e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 137812e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 137912e36acbSWarner Losh struct ieee80211_frame *wh; 138012e36acbSWarner Losh struct ieee80211_node *ni; 138112e36acbSWarner Losh struct ieee80211_key *k; 138212e36acbSWarner Losh struct mbuf *m; 138312e36acbSWarner Losh int trans, idx; 138412e36acbSWarner Losh 138512e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 138612e36acbSWarner Losh return; 138712e36acbSWarner Losh 138812e36acbSWarner Losh trans = 0; 138912e36acbSWarner Losh idx = tbd->tbd_idx; 139012e36acbSWarner Losh 139112e36acbSWarner Losh while (tbd->tbd_buf[idx].tb_mbuf == NULL) { 139212e36acbSWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m); /* XXX: LOCK */ 139312e36acbSWarner Losh if (m == NULL) 139412e36acbSWarner Losh break; 139512e36acbSWarner Losh 139612e36acbSWarner Losh ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 139712e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame *); 139812e36acbSWarner Losh if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 139912e36acbSWarner Losh k = ieee80211_crypto_encap(ni, m); 140012e36acbSWarner Losh if (k == NULL) { 140112e36acbSWarner Losh ieee80211_free_node(ni); 140212e36acbSWarner Losh m_freem(m); 140312e36acbSWarner Losh ifp->if_oerrors++; 140412e36acbSWarner Losh continue; 140512e36acbSWarner Losh } 140612e36acbSWarner Losh } 140712e36acbSWarner Losh wh = NULL; /* Catch any invalid use */ 140812e36acbSWarner Losh 140912e36acbSWarner Losh if (bwi_encap(sc, idx, m, ni) != 0) { 141012e36acbSWarner Losh /* 'm' is freed in bwi_encap() if we reach here */ 141112e36acbSWarner Losh if (ni != NULL) 141212e36acbSWarner Losh ieee80211_free_node(ni); 141312e36acbSWarner Losh ifp->if_oerrors++; 141412e36acbSWarner Losh continue; 141512e36acbSWarner Losh } 141612e36acbSWarner Losh 141712e36acbSWarner Losh trans = 1; 141812e36acbSWarner Losh tbd->tbd_used++; 141912e36acbSWarner Losh idx = (idx + 1) % BWI_TX_NDESC; 142012e36acbSWarner Losh 142112e36acbSWarner Losh ifp->if_opackets++; 142212e36acbSWarner Losh 142312e36acbSWarner Losh if (tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) { 142412e36acbSWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 142512e36acbSWarner Losh break; 142612e36acbSWarner Losh } 142712e36acbSWarner Losh } 142812e36acbSWarner Losh tbd->tbd_idx = idx; 142912e36acbSWarner Losh 143012e36acbSWarner Losh if (trans) 143158fbe5abSJohn Baldwin sc->sc_tx_timer = 5; 143212e36acbSWarner Losh } 143312e36acbSWarner Losh 143412e36acbSWarner Losh static int 143512e36acbSWarner Losh bwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 143612e36acbSWarner Losh const struct ieee80211_bpf_params *params) 143712e36acbSWarner Losh { 143812e36acbSWarner Losh struct ieee80211com *ic = ni->ni_ic; 143912e36acbSWarner Losh struct ifnet *ifp = ic->ic_ifp; 144012e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 144112e36acbSWarner Losh /* XXX wme? */ 144212e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 144312e36acbSWarner Losh int idx, error; 144412e36acbSWarner Losh 144512e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 144612e36acbSWarner Losh ieee80211_free_node(ni); 144712e36acbSWarner Losh m_freem(m); 144812e36acbSWarner Losh return ENETDOWN; 144912e36acbSWarner Losh } 145012e36acbSWarner Losh 145112e36acbSWarner Losh BWI_LOCK(sc); 145212e36acbSWarner Losh idx = tbd->tbd_idx; 145312e36acbSWarner Losh KASSERT(tbd->tbd_buf[idx].tb_mbuf == NULL, ("slot %d not empty", idx)); 145412e36acbSWarner Losh if (params == NULL) { 145512e36acbSWarner Losh /* 145612e36acbSWarner Losh * Legacy path; interpret frame contents to decide 145712e36acbSWarner Losh * precisely how to send the frame. 145812e36acbSWarner Losh */ 145912e36acbSWarner Losh error = bwi_encap(sc, idx, m, ni); 146012e36acbSWarner Losh } else { 146112e36acbSWarner Losh /* 146212e36acbSWarner Losh * Caller supplied explicit parameters to use in 146312e36acbSWarner Losh * sending the frame. 146412e36acbSWarner Losh */ 146512e36acbSWarner Losh error = bwi_encap_raw(sc, idx, m, ni, params); 146612e36acbSWarner Losh } 146712e36acbSWarner Losh if (error == 0) { 146812e36acbSWarner Losh ifp->if_opackets++; 146912e36acbSWarner Losh if (++tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) 147012e36acbSWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 147112e36acbSWarner Losh tbd->tbd_idx = (idx + 1) % BWI_TX_NDESC; 147258fbe5abSJohn Baldwin sc->sc_tx_timer = 5; 147312e36acbSWarner Losh } else { 147412e36acbSWarner Losh /* NB: m is reclaimed on encap failure */ 147512e36acbSWarner Losh ieee80211_free_node(ni); 147612e36acbSWarner Losh ifp->if_oerrors++; 147712e36acbSWarner Losh } 147812e36acbSWarner Losh BWI_UNLOCK(sc); 147912e36acbSWarner Losh return error; 148012e36acbSWarner Losh } 148112e36acbSWarner Losh 148212e36acbSWarner Losh static void 148358fbe5abSJohn Baldwin bwi_watchdog(void *arg) 148412e36acbSWarner Losh { 148558fbe5abSJohn Baldwin struct bwi_softc *sc; 148658fbe5abSJohn Baldwin struct ifnet *ifp; 148712e36acbSWarner Losh 148858fbe5abSJohn Baldwin sc = arg; 148958fbe5abSJohn Baldwin ifp = sc->sc_ifp; 149058fbe5abSJohn Baldwin BWI_ASSERT_LOCKED(sc); 149158fbe5abSJohn Baldwin if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) { 149212e36acbSWarner Losh if_printf(ifp, "watchdog timeout\n"); 149312e36acbSWarner Losh ifp->if_oerrors++; 149412e36acbSWarner Losh taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); 149512e36acbSWarner Losh } 149658fbe5abSJohn Baldwin callout_reset(&sc->sc_watchdog_timer, hz, bwi_watchdog, sc); 149712e36acbSWarner Losh } 149812e36acbSWarner Losh 149912e36acbSWarner Losh static void 150012e36acbSWarner Losh bwi_stop(struct bwi_softc *sc, int statechg) 150112e36acbSWarner Losh { 150212e36acbSWarner Losh BWI_LOCK(sc); 150312e36acbSWarner Losh bwi_stop_locked(sc, statechg); 150412e36acbSWarner Losh BWI_UNLOCK(sc); 150512e36acbSWarner Losh } 150612e36acbSWarner Losh 150712e36acbSWarner Losh static void 150812e36acbSWarner Losh bwi_stop_locked(struct bwi_softc *sc, int statechg) 150912e36acbSWarner Losh { 151012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 151112e36acbSWarner Losh struct bwi_mac *mac; 151212e36acbSWarner Losh int i, error, pwr_off = 0; 151312e36acbSWarner Losh 151412e36acbSWarner Losh BWI_ASSERT_LOCKED(sc); 151512e36acbSWarner Losh 151612e36acbSWarner Losh callout_stop(&sc->sc_calib_ch); 151712e36acbSWarner Losh callout_stop(&sc->sc_led_blink_ch); 151812e36acbSWarner Losh sc->sc_led_blinking = 0; 151912e36acbSWarner Losh sc->sc_flags |= BWI_F_STOP; 152012e36acbSWarner Losh 152112e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 152212e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 152312e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 152412e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 152512e36acbSWarner Losh 152612e36acbSWarner Losh bwi_disable_intrs(sc, BWI_ALL_INTRS); 152712e36acbSWarner Losh CSR_READ_4(sc, BWI_MAC_INTR_MASK); 152812e36acbSWarner Losh bwi_mac_stop(mac); 152912e36acbSWarner Losh } 153012e36acbSWarner Losh 153112e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) { 153212e36acbSWarner Losh struct bwi_regwin *old_rw; 153312e36acbSWarner Losh 153412e36acbSWarner Losh mac = &sc->sc_mac[i]; 153512e36acbSWarner Losh if ((mac->mac_flags & BWI_MAC_F_INITED) == 0) 153612e36acbSWarner Losh continue; 153712e36acbSWarner Losh 153812e36acbSWarner Losh error = bwi_regwin_switch(sc, &mac->mac_regwin, &old_rw); 153912e36acbSWarner Losh if (error) 154012e36acbSWarner Losh continue; 154112e36acbSWarner Losh 154212e36acbSWarner Losh bwi_mac_shutdown(mac); 154312e36acbSWarner Losh pwr_off = 1; 154412e36acbSWarner Losh 154512e36acbSWarner Losh bwi_regwin_switch(sc, old_rw, NULL); 154612e36acbSWarner Losh } 154712e36acbSWarner Losh 154812e36acbSWarner Losh if (pwr_off) 154912e36acbSWarner Losh bwi_bbp_power_off(sc); 155012e36acbSWarner Losh 155112e36acbSWarner Losh sc->sc_tx_timer = 0; 155258fbe5abSJohn Baldwin callout_stop(&sc->sc_watchdog_timer); 155312e36acbSWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 155412e36acbSWarner Losh } 155512e36acbSWarner Losh 155612e36acbSWarner Losh void 155712e36acbSWarner Losh bwi_intr(void *xsc) 155812e36acbSWarner Losh { 155912e36acbSWarner Losh struct bwi_softc *sc = xsc; 156012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 156112e36acbSWarner Losh struct bwi_mac *mac; 156212e36acbSWarner Losh uint32_t intr_status; 156312e36acbSWarner Losh uint32_t txrx_intr_status[BWI_TXRX_NRING]; 156412e36acbSWarner Losh int i, txrx_error, tx = 0, rx_data = -1; 156512e36acbSWarner Losh 156612e36acbSWarner Losh BWI_LOCK(sc); 156712e36acbSWarner Losh 156812e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 156912e36acbSWarner Losh (sc->sc_flags & BWI_F_STOP)) { 157012e36acbSWarner Losh BWI_UNLOCK(sc); 157112e36acbSWarner Losh return; 157212e36acbSWarner Losh } 157312e36acbSWarner Losh /* 157412e36acbSWarner Losh * Get interrupt status 157512e36acbSWarner Losh */ 157612e36acbSWarner Losh intr_status = CSR_READ_4(sc, BWI_MAC_INTR_STATUS); 157712e36acbSWarner Losh if (intr_status == 0xffffffff) { /* Not for us */ 157812e36acbSWarner Losh BWI_UNLOCK(sc); 157912e36acbSWarner Losh return; 158012e36acbSWarner Losh } 158112e36acbSWarner Losh 158212e36acbSWarner Losh DPRINTF(sc, BWI_DBG_INTR, "intr status 0x%08x\n", intr_status); 158312e36acbSWarner Losh 158412e36acbSWarner Losh intr_status &= CSR_READ_4(sc, BWI_MAC_INTR_MASK); 158512e36acbSWarner Losh if (intr_status == 0) { /* Nothing is interesting */ 158612e36acbSWarner Losh BWI_UNLOCK(sc); 158712e36acbSWarner Losh return; 158812e36acbSWarner Losh } 158912e36acbSWarner Losh 159012e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 159112e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 159212e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 159312e36acbSWarner Losh 159412e36acbSWarner Losh txrx_error = 0; 159512e36acbSWarner Losh DPRINTF(sc, BWI_DBG_INTR, "%s\n", "TX/RX intr"); 159612e36acbSWarner Losh for (i = 0; i < BWI_TXRX_NRING; ++i) { 159712e36acbSWarner Losh uint32_t mask; 159812e36acbSWarner Losh 159912e36acbSWarner Losh if (BWI_TXRX_IS_RX(i)) 160012e36acbSWarner Losh mask = BWI_TXRX_RX_INTRS; 160112e36acbSWarner Losh else 160212e36acbSWarner Losh mask = BWI_TXRX_TX_INTRS; 160312e36acbSWarner Losh 160412e36acbSWarner Losh txrx_intr_status[i] = 160512e36acbSWarner Losh CSR_READ_4(sc, BWI_TXRX_INTR_STATUS(i)) & mask; 160612e36acbSWarner Losh 160712e36acbSWarner Losh _DPRINTF(sc, BWI_DBG_INTR, ", %d 0x%08x", 160812e36acbSWarner Losh i, txrx_intr_status[i]); 160912e36acbSWarner Losh 161012e36acbSWarner Losh if (txrx_intr_status[i] & BWI_TXRX_INTR_ERROR) { 161112e36acbSWarner Losh if_printf(ifp, 161212e36acbSWarner Losh "%s: intr fatal TX/RX (%d) error 0x%08x\n", 161312e36acbSWarner Losh __func__, i, txrx_intr_status[i]); 161412e36acbSWarner Losh txrx_error = 1; 161512e36acbSWarner Losh } 161612e36acbSWarner Losh } 161712e36acbSWarner Losh _DPRINTF(sc, BWI_DBG_INTR, "%s\n", ""); 161812e36acbSWarner Losh 161912e36acbSWarner Losh /* 162012e36acbSWarner Losh * Acknowledge interrupt 162112e36acbSWarner Losh */ 162212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, intr_status); 162312e36acbSWarner Losh 162412e36acbSWarner Losh for (i = 0; i < BWI_TXRX_NRING; ++i) 162512e36acbSWarner Losh CSR_WRITE_4(sc, BWI_TXRX_INTR_STATUS(i), txrx_intr_status[i]); 162612e36acbSWarner Losh 162712e36acbSWarner Losh /* Disable all interrupts */ 162812e36acbSWarner Losh bwi_disable_intrs(sc, BWI_ALL_INTRS); 162912e36acbSWarner Losh 16301771d207SWarner Losh /* 16311771d207SWarner Losh * http://bcm-specs.sipsolutions.net/Interrupts 16321771d207SWarner Losh * Says for this bit (0x800): 16331771d207SWarner Losh * "Fatal Error 16341771d207SWarner Losh * 16351771d207SWarner Losh * We got this one while testing things when by accident the 16361771d207SWarner Losh * template ram wasn't set to big endian when it should have 16371771d207SWarner Losh * been after writing the initial values. It keeps on being 16381771d207SWarner Losh * triggered, the only way to stop it seems to shut down the 16391771d207SWarner Losh * chip." 16401771d207SWarner Losh * 16411771d207SWarner Losh * Suggesting that we should never get it and if we do we're not 16421771d207SWarner Losh * feeding TX packets into the MAC correctly if we do... Apparently, 16431771d207SWarner Losh * it is valid only on mac version 5 and higher, but I couldn't 16441771d207SWarner Losh * find a reference for that... Since I see them from time to time 16451771d207SWarner Losh * on my card, this suggests an error in the tx path still... 16461771d207SWarner Losh */ 164712e36acbSWarner Losh if (intr_status & BWI_INTR_PHY_TXERR) { 164812e36acbSWarner Losh if (mac->mac_flags & BWI_MAC_F_PHYE_RESET) { 164912e36acbSWarner Losh if_printf(ifp, "%s: intr PHY TX error\n", __func__); 165012e36acbSWarner Losh taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); 165112e36acbSWarner Losh BWI_UNLOCK(sc); 165212e36acbSWarner Losh return; 165312e36acbSWarner Losh } 165412e36acbSWarner Losh } 165512e36acbSWarner Losh 165612e36acbSWarner Losh if (txrx_error) { 165712e36acbSWarner Losh /* TODO: reset device */ 165812e36acbSWarner Losh } 165912e36acbSWarner Losh 166012e36acbSWarner Losh if (intr_status & BWI_INTR_TBTT) 166112e36acbSWarner Losh bwi_mac_config_ps(mac); 166212e36acbSWarner Losh 166312e36acbSWarner Losh if (intr_status & BWI_INTR_EO_ATIM) 166412e36acbSWarner Losh if_printf(ifp, "EO_ATIM\n"); 166512e36acbSWarner Losh 166612e36acbSWarner Losh if (intr_status & BWI_INTR_PMQ) { 166712e36acbSWarner Losh for (;;) { 166812e36acbSWarner Losh if ((CSR_READ_4(sc, BWI_MAC_PS_STATUS) & 0x8) == 0) 166912e36acbSWarner Losh break; 167012e36acbSWarner Losh } 167112e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_PS_STATUS, 0x2); 167212e36acbSWarner Losh } 167312e36acbSWarner Losh 167412e36acbSWarner Losh if (intr_status & BWI_INTR_NOISE) 167512e36acbSWarner Losh if_printf(ifp, "intr noise\n"); 167612e36acbSWarner Losh 167712e36acbSWarner Losh if (txrx_intr_status[0] & BWI_TXRX_INTR_RX) { 167812e36acbSWarner Losh rx_data = sc->sc_rxeof(sc); 167912e36acbSWarner Losh if (sc->sc_flags & BWI_F_STOP) { 168012e36acbSWarner Losh BWI_UNLOCK(sc); 168112e36acbSWarner Losh return; 168212e36acbSWarner Losh } 168312e36acbSWarner Losh } 168412e36acbSWarner Losh 168512e36acbSWarner Losh if (txrx_intr_status[3] & BWI_TXRX_INTR_RX) { 168612e36acbSWarner Losh sc->sc_txeof_status(sc); 168712e36acbSWarner Losh tx = 1; 168812e36acbSWarner Losh } 168912e36acbSWarner Losh 169012e36acbSWarner Losh if (intr_status & BWI_INTR_TX_DONE) { 169112e36acbSWarner Losh bwi_txeof(sc); 169212e36acbSWarner Losh tx = 1; 169312e36acbSWarner Losh } 169412e36acbSWarner Losh 169512e36acbSWarner Losh /* Re-enable interrupts */ 169612e36acbSWarner Losh bwi_enable_intrs(sc, BWI_INIT_INTRS); 169712e36acbSWarner Losh 169812e36acbSWarner Losh if (sc->sc_blink_led != NULL && sc->sc_led_blink) { 169912e36acbSWarner Losh int evt = BWI_LED_EVENT_NONE; 170012e36acbSWarner Losh 170112e36acbSWarner Losh if (tx && rx_data > 0) { 170212e36acbSWarner Losh if (sc->sc_rx_rate > sc->sc_tx_rate) 170312e36acbSWarner Losh evt = BWI_LED_EVENT_RX; 170412e36acbSWarner Losh else 170512e36acbSWarner Losh evt = BWI_LED_EVENT_TX; 170612e36acbSWarner Losh } else if (tx) { 170712e36acbSWarner Losh evt = BWI_LED_EVENT_TX; 170812e36acbSWarner Losh } else if (rx_data > 0) { 170912e36acbSWarner Losh evt = BWI_LED_EVENT_RX; 171012e36acbSWarner Losh } else if (rx_data == 0) { 171112e36acbSWarner Losh evt = BWI_LED_EVENT_POLL; 171212e36acbSWarner Losh } 171312e36acbSWarner Losh 171412e36acbSWarner Losh if (evt != BWI_LED_EVENT_NONE) 171512e36acbSWarner Losh bwi_led_event(sc, evt); 171612e36acbSWarner Losh } 171712e36acbSWarner Losh 171812e36acbSWarner Losh BWI_UNLOCK(sc); 171912e36acbSWarner Losh } 172012e36acbSWarner Losh 172112e36acbSWarner Losh static void 172212e36acbSWarner Losh bwi_scan_start(struct ieee80211com *ic) 172312e36acbSWarner Losh { 172412e36acbSWarner Losh struct bwi_softc *sc = ic->ic_ifp->if_softc; 172512e36acbSWarner Losh 172612e36acbSWarner Losh BWI_LOCK(sc); 172712e36acbSWarner Losh /* Enable MAC beacon promiscuity */ 172812e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PASS_BCN); 172912e36acbSWarner Losh BWI_UNLOCK(sc); 173012e36acbSWarner Losh } 173112e36acbSWarner Losh 173212e36acbSWarner Losh static void 173312e36acbSWarner Losh bwi_set_channel(struct ieee80211com *ic) 173412e36acbSWarner Losh { 173512e36acbSWarner Losh struct bwi_softc *sc = ic->ic_ifp->if_softc; 173612e36acbSWarner Losh struct ieee80211_channel *c = ic->ic_curchan; 173712e36acbSWarner Losh struct bwi_mac *mac; 173812e36acbSWarner Losh 173912e36acbSWarner Losh BWI_LOCK(sc); 174012e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 174112e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 174212e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 174312e36acbSWarner Losh bwi_rf_set_chan(mac, ieee80211_chan2ieee(ic, c), 0); 174412e36acbSWarner Losh 174512e36acbSWarner Losh sc->sc_rates = ieee80211_get_ratetable(c); 174612e36acbSWarner Losh 174712e36acbSWarner Losh /* 174812e36acbSWarner Losh * Setup radio tap channel freq and flags 174912e36acbSWarner Losh */ 175012e36acbSWarner Losh sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = 175112e36acbSWarner Losh htole16(c->ic_freq); 175212e36acbSWarner Losh sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = 175312e36acbSWarner Losh htole16(c->ic_flags & 0xffff); 175412e36acbSWarner Losh 175512e36acbSWarner Losh BWI_UNLOCK(sc); 175612e36acbSWarner Losh } 175712e36acbSWarner Losh 175812e36acbSWarner Losh static void 175912e36acbSWarner Losh bwi_scan_end(struct ieee80211com *ic) 176012e36acbSWarner Losh { 176112e36acbSWarner Losh struct bwi_softc *sc = ic->ic_ifp->if_softc; 176212e36acbSWarner Losh 176312e36acbSWarner Losh BWI_LOCK(sc); 176412e36acbSWarner Losh CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PASS_BCN); 176512e36acbSWarner Losh BWI_UNLOCK(sc); 176612e36acbSWarner Losh } 176712e36acbSWarner Losh 176812e36acbSWarner Losh static int 176912e36acbSWarner Losh bwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 177012e36acbSWarner Losh { 177112e36acbSWarner Losh struct bwi_vap *bvp = BWI_VAP(vap); 177237c3e522SWarner Losh struct ieee80211com *ic= vap->iv_ic; 177337c3e522SWarner Losh struct ifnet *ifp = ic->ic_ifp; 177437c3e522SWarner Losh enum ieee80211_state ostate = vap->iv_state; 177512e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 177612e36acbSWarner Losh struct bwi_mac *mac; 177737c3e522SWarner Losh struct ieee80211_node *ni = vap->iv_bss; 177812e36acbSWarner Losh int error; 177912e36acbSWarner Losh 178012e36acbSWarner Losh BWI_LOCK(sc); 178112e36acbSWarner Losh 178212e36acbSWarner Losh callout_stop(&sc->sc_calib_ch); 178312e36acbSWarner Losh 178412e36acbSWarner Losh if (nstate == IEEE80211_S_INIT) 178512e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_INIT; 178612e36acbSWarner Losh 178712e36acbSWarner Losh bwi_led_newstate(sc, nstate); 178812e36acbSWarner Losh 178912e36acbSWarner Losh error = bvp->bv_newstate(vap, nstate, arg); 179012e36acbSWarner Losh if (error != 0) 179112e36acbSWarner Losh goto back; 179212e36acbSWarner Losh 179337c3e522SWarner Losh /* 179437c3e522SWarner Losh * Clear the BSSID when we stop a STA 179537c3e522SWarner Losh */ 179637c3e522SWarner Losh if (vap->iv_opmode == IEEE80211_M_STA) { 179737c3e522SWarner Losh if (ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) { 179837c3e522SWarner Losh /* 179937c3e522SWarner Losh * Clear out the BSSID. If we reassociate to 180037c3e522SWarner Losh * the same AP, this will reinialize things 180137c3e522SWarner Losh * correctly... 180237c3e522SWarner Losh */ 180337c3e522SWarner Losh if (ic->ic_opmode == IEEE80211_M_STA && 180437c3e522SWarner Losh !(sc->sc_flags & BWI_F_STOP)) 180537c3e522SWarner Losh bwi_set_bssid(sc, bwi_zero_addr); 180637c3e522SWarner Losh } 180737c3e522SWarner Losh } 180837c3e522SWarner Losh 180912e36acbSWarner Losh if (vap->iv_opmode == IEEE80211_M_MONITOR) { 181012e36acbSWarner Losh /* Nothing to do */ 181112e36acbSWarner Losh } else if (nstate == IEEE80211_S_RUN) { 181212e36acbSWarner Losh bwi_set_bssid(sc, vap->iv_bss->ni_bssid); 181312e36acbSWarner Losh 181412e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 181512e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 181612e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 181712e36acbSWarner Losh 181812e36acbSWarner Losh /* Initial TX power calibration */ 181912e36acbSWarner Losh bwi_mac_calibrate_txpower(mac, BWI_TXPWR_INIT); 182012e36acbSWarner Losh #ifdef notyet 182112e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_FORCE; 182212e36acbSWarner Losh #else 182312e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; 182412e36acbSWarner Losh #endif 182512e36acbSWarner Losh if (vap->iv_opmode == IEEE80211_M_STA) { 182612e36acbSWarner Losh /* fake a join to init the tx rate */ 1827b6108616SRui Paulo ic->ic_newassoc(ni, 1); 182812e36acbSWarner Losh } 182912e36acbSWarner Losh 183012e36acbSWarner Losh callout_reset(&sc->sc_calib_ch, hz, bwi_calibrate, sc); 183112e36acbSWarner Losh } 183212e36acbSWarner Losh back: 183312e36acbSWarner Losh BWI_UNLOCK(sc); 183412e36acbSWarner Losh 183512e36acbSWarner Losh return error; 183612e36acbSWarner Losh } 183712e36acbSWarner Losh 183812e36acbSWarner Losh static int 183912e36acbSWarner Losh bwi_media_change(struct ifnet *ifp) 184012e36acbSWarner Losh { 184112e36acbSWarner Losh int error = ieee80211_media_change(ifp); 184212e36acbSWarner Losh /* NB: only the fixed rate can change and that doesn't need a reset */ 184312e36acbSWarner Losh return (error == ENETRESET ? 0 : error); 184412e36acbSWarner Losh } 184512e36acbSWarner Losh 184612e36acbSWarner Losh static int 184712e36acbSWarner Losh bwi_dma_alloc(struct bwi_softc *sc) 184812e36acbSWarner Losh { 184912e36acbSWarner Losh int error, i, has_txstats; 185012e36acbSWarner Losh bus_addr_t lowaddr = 0; 185112e36acbSWarner Losh bus_size_t tx_ring_sz, rx_ring_sz, desc_sz = 0; 185212e36acbSWarner Losh uint32_t txrx_ctrl_step = 0; 185312e36acbSWarner Losh 185412e36acbSWarner Losh has_txstats = 0; 185512e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) { 185612e36acbSWarner Losh if (sc->sc_mac[i].mac_flags & BWI_MAC_F_HAS_TXSTATS) { 185712e36acbSWarner Losh has_txstats = 1; 185812e36acbSWarner Losh break; 185912e36acbSWarner Losh } 186012e36acbSWarner Losh } 186112e36acbSWarner Losh 186212e36acbSWarner Losh switch (sc->sc_bus_space) { 186312e36acbSWarner Losh case BWI_BUS_SPACE_30BIT: 186412e36acbSWarner Losh case BWI_BUS_SPACE_32BIT: 186512e36acbSWarner Losh if (sc->sc_bus_space == BWI_BUS_SPACE_30BIT) 186612e36acbSWarner Losh lowaddr = BWI_BUS_SPACE_MAXADDR; 186712e36acbSWarner Losh else 186812e36acbSWarner Losh lowaddr = BUS_SPACE_MAXADDR_32BIT; 186912e36acbSWarner Losh desc_sz = sizeof(struct bwi_desc32); 187012e36acbSWarner Losh txrx_ctrl_step = 0x20; 187112e36acbSWarner Losh 187212e36acbSWarner Losh sc->sc_init_tx_ring = bwi_init_tx_ring32; 187312e36acbSWarner Losh sc->sc_free_tx_ring = bwi_free_tx_ring32; 187412e36acbSWarner Losh sc->sc_init_rx_ring = bwi_init_rx_ring32; 187512e36acbSWarner Losh sc->sc_free_rx_ring = bwi_free_rx_ring32; 187612e36acbSWarner Losh sc->sc_setup_rxdesc = bwi_setup_rx_desc32; 187712e36acbSWarner Losh sc->sc_setup_txdesc = bwi_setup_tx_desc32; 187812e36acbSWarner Losh sc->sc_rxeof = bwi_rxeof32; 187912e36acbSWarner Losh sc->sc_start_tx = bwi_start_tx32; 188012e36acbSWarner Losh if (has_txstats) { 188112e36acbSWarner Losh sc->sc_init_txstats = bwi_init_txstats32; 188212e36acbSWarner Losh sc->sc_free_txstats = bwi_free_txstats32; 188312e36acbSWarner Losh sc->sc_txeof_status = bwi_txeof_status32; 188412e36acbSWarner Losh } 188512e36acbSWarner Losh break; 188612e36acbSWarner Losh 188712e36acbSWarner Losh case BWI_BUS_SPACE_64BIT: 188812e36acbSWarner Losh lowaddr = BUS_SPACE_MAXADDR; /* XXX */ 188912e36acbSWarner Losh desc_sz = sizeof(struct bwi_desc64); 189012e36acbSWarner Losh txrx_ctrl_step = 0x40; 189112e36acbSWarner Losh 189212e36acbSWarner Losh sc->sc_init_tx_ring = bwi_init_tx_ring64; 189312e36acbSWarner Losh sc->sc_free_tx_ring = bwi_free_tx_ring64; 189412e36acbSWarner Losh sc->sc_init_rx_ring = bwi_init_rx_ring64; 189512e36acbSWarner Losh sc->sc_free_rx_ring = bwi_free_rx_ring64; 189612e36acbSWarner Losh sc->sc_setup_rxdesc = bwi_setup_rx_desc64; 189712e36acbSWarner Losh sc->sc_setup_txdesc = bwi_setup_tx_desc64; 189812e36acbSWarner Losh sc->sc_rxeof = bwi_rxeof64; 189912e36acbSWarner Losh sc->sc_start_tx = bwi_start_tx64; 190012e36acbSWarner Losh if (has_txstats) { 190112e36acbSWarner Losh sc->sc_init_txstats = bwi_init_txstats64; 190212e36acbSWarner Losh sc->sc_free_txstats = bwi_free_txstats64; 190312e36acbSWarner Losh sc->sc_txeof_status = bwi_txeof_status64; 190412e36acbSWarner Losh } 190512e36acbSWarner Losh break; 190612e36acbSWarner Losh } 190712e36acbSWarner Losh 190812e36acbSWarner Losh KASSERT(lowaddr != 0, ("lowaddr zero")); 190912e36acbSWarner Losh KASSERT(desc_sz != 0, ("desc_sz zero")); 191012e36acbSWarner Losh KASSERT(txrx_ctrl_step != 0, ("txrx_ctrl_step zero")); 191112e36acbSWarner Losh 191212e36acbSWarner Losh tx_ring_sz = roundup(desc_sz * BWI_TX_NDESC, BWI_RING_ALIGN); 191312e36acbSWarner Losh rx_ring_sz = roundup(desc_sz * BWI_RX_NDESC, BWI_RING_ALIGN); 191412e36acbSWarner Losh 191512e36acbSWarner Losh /* 191612e36acbSWarner Losh * Create top level DMA tag 191712e36acbSWarner Losh */ 191812e36acbSWarner Losh error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ 191912e36acbSWarner Losh BWI_ALIGN, 0, /* alignment, bounds */ 192012e36acbSWarner Losh lowaddr, /* lowaddr */ 192112e36acbSWarner Losh BUS_SPACE_MAXADDR, /* highaddr */ 192212e36acbSWarner Losh NULL, NULL, /* filter, filterarg */ 192312e36acbSWarner Losh MAXBSIZE, /* maxsize */ 192412e36acbSWarner Losh BUS_SPACE_UNRESTRICTED, /* nsegments */ 192512e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 192612e36acbSWarner Losh BUS_DMA_ALLOCNOW, /* flags */ 192712e36acbSWarner Losh NULL, NULL, /* lockfunc, lockarg */ 192812e36acbSWarner Losh &sc->sc_parent_dtag); 192912e36acbSWarner Losh if (error) { 193012e36acbSWarner Losh device_printf(sc->sc_dev, "can't create parent DMA tag\n"); 193112e36acbSWarner Losh return error; 193212e36acbSWarner Losh } 193312e36acbSWarner Losh 193412e36acbSWarner Losh #define TXRX_CTRL(idx) (BWI_TXRX_CTRL_BASE + (idx) * txrx_ctrl_step) 193512e36acbSWarner Losh 193612e36acbSWarner Losh /* 193712e36acbSWarner Losh * Create TX ring DMA stuffs 193812e36acbSWarner Losh */ 193912e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 194012e36acbSWarner Losh BWI_RING_ALIGN, 0, 194112e36acbSWarner Losh BUS_SPACE_MAXADDR, 194212e36acbSWarner Losh BUS_SPACE_MAXADDR, 194312e36acbSWarner Losh NULL, NULL, 194412e36acbSWarner Losh tx_ring_sz, 194512e36acbSWarner Losh 1, 194612e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, 194712e36acbSWarner Losh BUS_DMA_ALLOCNOW, 194812e36acbSWarner Losh NULL, NULL, 194912e36acbSWarner Losh &sc->sc_txring_dtag); 195012e36acbSWarner Losh if (error) { 195112e36acbSWarner Losh device_printf(sc->sc_dev, "can't create TX ring DMA tag\n"); 195212e36acbSWarner Losh return error; 195312e36acbSWarner Losh } 195412e36acbSWarner Losh 195512e36acbSWarner Losh for (i = 0; i < BWI_TX_NRING; ++i) { 195612e36acbSWarner Losh error = bwi_dma_ring_alloc(sc, sc->sc_txring_dtag, 195712e36acbSWarner Losh &sc->sc_tx_rdata[i], tx_ring_sz, 195812e36acbSWarner Losh TXRX_CTRL(i)); 195912e36acbSWarner Losh if (error) { 196012e36acbSWarner Losh device_printf(sc->sc_dev, "%dth TX ring " 196112e36acbSWarner Losh "DMA alloc failed\n", i); 196212e36acbSWarner Losh return error; 196312e36acbSWarner Losh } 196412e36acbSWarner Losh } 196512e36acbSWarner Losh 196612e36acbSWarner Losh /* 196712e36acbSWarner Losh * Create RX ring DMA stuffs 196812e36acbSWarner Losh */ 196912e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 197012e36acbSWarner Losh BWI_RING_ALIGN, 0, 197112e36acbSWarner Losh BUS_SPACE_MAXADDR, 197212e36acbSWarner Losh BUS_SPACE_MAXADDR, 197312e36acbSWarner Losh NULL, NULL, 197412e36acbSWarner Losh rx_ring_sz, 197512e36acbSWarner Losh 1, 197612e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, 197712e36acbSWarner Losh BUS_DMA_ALLOCNOW, 197812e36acbSWarner Losh NULL, NULL, 197912e36acbSWarner Losh &sc->sc_rxring_dtag); 198012e36acbSWarner Losh if (error) { 198112e36acbSWarner Losh device_printf(sc->sc_dev, "can't create RX ring DMA tag\n"); 198212e36acbSWarner Losh return error; 198312e36acbSWarner Losh } 198412e36acbSWarner Losh 198512e36acbSWarner Losh error = bwi_dma_ring_alloc(sc, sc->sc_rxring_dtag, &sc->sc_rx_rdata, 198612e36acbSWarner Losh rx_ring_sz, TXRX_CTRL(0)); 198712e36acbSWarner Losh if (error) { 198812e36acbSWarner Losh device_printf(sc->sc_dev, "RX ring DMA alloc failed\n"); 198912e36acbSWarner Losh return error; 199012e36acbSWarner Losh } 199112e36acbSWarner Losh 199212e36acbSWarner Losh if (has_txstats) { 199312e36acbSWarner Losh error = bwi_dma_txstats_alloc(sc, TXRX_CTRL(3), desc_sz); 199412e36acbSWarner Losh if (error) { 199512e36acbSWarner Losh device_printf(sc->sc_dev, 199612e36acbSWarner Losh "TX stats DMA alloc failed\n"); 199712e36acbSWarner Losh return error; 199812e36acbSWarner Losh } 199912e36acbSWarner Losh } 200012e36acbSWarner Losh 200112e36acbSWarner Losh #undef TXRX_CTRL 200212e36acbSWarner Losh 200312e36acbSWarner Losh return bwi_dma_mbuf_create(sc); 200412e36acbSWarner Losh } 200512e36acbSWarner Losh 200612e36acbSWarner Losh static void 200712e36acbSWarner Losh bwi_dma_free(struct bwi_softc *sc) 200812e36acbSWarner Losh { 200912e36acbSWarner Losh if (sc->sc_txring_dtag != NULL) { 201012e36acbSWarner Losh int i; 201112e36acbSWarner Losh 201212e36acbSWarner Losh for (i = 0; i < BWI_TX_NRING; ++i) { 201312e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_tx_rdata[i]; 201412e36acbSWarner Losh 201512e36acbSWarner Losh if (rd->rdata_desc != NULL) { 201612e36acbSWarner Losh bus_dmamap_unload(sc->sc_txring_dtag, 201712e36acbSWarner Losh rd->rdata_dmap); 201812e36acbSWarner Losh bus_dmamem_free(sc->sc_txring_dtag, 201912e36acbSWarner Losh rd->rdata_desc, 202012e36acbSWarner Losh rd->rdata_dmap); 202112e36acbSWarner Losh } 202212e36acbSWarner Losh } 202312e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_txring_dtag); 202412e36acbSWarner Losh } 202512e36acbSWarner Losh 202612e36acbSWarner Losh if (sc->sc_rxring_dtag != NULL) { 202712e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 202812e36acbSWarner Losh 202912e36acbSWarner Losh if (rd->rdata_desc != NULL) { 203012e36acbSWarner Losh bus_dmamap_unload(sc->sc_rxring_dtag, rd->rdata_dmap); 203112e36acbSWarner Losh bus_dmamem_free(sc->sc_rxring_dtag, rd->rdata_desc, 203212e36acbSWarner Losh rd->rdata_dmap); 203312e36acbSWarner Losh } 203412e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_rxring_dtag); 203512e36acbSWarner Losh } 203612e36acbSWarner Losh 203712e36acbSWarner Losh bwi_dma_txstats_free(sc); 203812e36acbSWarner Losh bwi_dma_mbuf_destroy(sc, BWI_TX_NRING, 1); 203912e36acbSWarner Losh 204012e36acbSWarner Losh if (sc->sc_parent_dtag != NULL) 204112e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_parent_dtag); 204212e36acbSWarner Losh } 204312e36acbSWarner Losh 204412e36acbSWarner Losh static int 204512e36acbSWarner Losh bwi_dma_ring_alloc(struct bwi_softc *sc, bus_dma_tag_t dtag, 204612e36acbSWarner Losh struct bwi_ring_data *rd, bus_size_t size, 204712e36acbSWarner Losh uint32_t txrx_ctrl) 204812e36acbSWarner Losh { 204912e36acbSWarner Losh int error; 205012e36acbSWarner Losh 205112e36acbSWarner Losh error = bus_dmamem_alloc(dtag, &rd->rdata_desc, 205212e36acbSWarner Losh BUS_DMA_WAITOK | BUS_DMA_ZERO, 205312e36acbSWarner Losh &rd->rdata_dmap); 205412e36acbSWarner Losh if (error) { 205512e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate DMA mem\n"); 205612e36acbSWarner Losh return error; 205712e36acbSWarner Losh } 205812e36acbSWarner Losh 205912e36acbSWarner Losh error = bus_dmamap_load(dtag, rd->rdata_dmap, rd->rdata_desc, size, 206012e36acbSWarner Losh bwi_dma_ring_addr, &rd->rdata_paddr, 206112e36acbSWarner Losh BUS_DMA_NOWAIT); 206212e36acbSWarner Losh if (error) { 206312e36acbSWarner Losh device_printf(sc->sc_dev, "can't load DMA mem\n"); 206412e36acbSWarner Losh bus_dmamem_free(dtag, rd->rdata_desc, rd->rdata_dmap); 206512e36acbSWarner Losh rd->rdata_desc = NULL; 206612e36acbSWarner Losh return error; 206712e36acbSWarner Losh } 206812e36acbSWarner Losh 206912e36acbSWarner Losh rd->rdata_txrx_ctrl = txrx_ctrl; 207012e36acbSWarner Losh return 0; 207112e36acbSWarner Losh } 207212e36acbSWarner Losh 207312e36acbSWarner Losh static int 207412e36acbSWarner Losh bwi_dma_txstats_alloc(struct bwi_softc *sc, uint32_t ctrl_base, 207512e36acbSWarner Losh bus_size_t desc_sz) 207612e36acbSWarner Losh { 207712e36acbSWarner Losh struct bwi_txstats_data *st; 207812e36acbSWarner Losh bus_size_t dma_size; 207912e36acbSWarner Losh int error; 208012e36acbSWarner Losh 208112e36acbSWarner Losh st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT | M_ZERO); 208212e36acbSWarner Losh if (st == NULL) { 208312e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate txstats data\n"); 208412e36acbSWarner Losh return ENOMEM; 208512e36acbSWarner Losh } 208612e36acbSWarner Losh sc->sc_txstats = st; 208712e36acbSWarner Losh 208812e36acbSWarner Losh /* 208912e36acbSWarner Losh * Create TX stats descriptor DMA stuffs 209012e36acbSWarner Losh */ 209112e36acbSWarner Losh dma_size = roundup(desc_sz * BWI_TXSTATS_NDESC, BWI_RING_ALIGN); 209212e36acbSWarner Losh 209312e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 209412e36acbSWarner Losh BWI_RING_ALIGN, 209512e36acbSWarner Losh 0, 209612e36acbSWarner Losh BUS_SPACE_MAXADDR, 209712e36acbSWarner Losh BUS_SPACE_MAXADDR, 209812e36acbSWarner Losh NULL, NULL, 209912e36acbSWarner Losh dma_size, 210012e36acbSWarner Losh 1, 210112e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, 210212e36acbSWarner Losh BUS_DMA_ALLOCNOW, 210312e36acbSWarner Losh NULL, NULL, 210412e36acbSWarner Losh &st->stats_ring_dtag); 210512e36acbSWarner Losh if (error) { 210612e36acbSWarner Losh device_printf(sc->sc_dev, "can't create txstats ring " 210712e36acbSWarner Losh "DMA tag\n"); 210812e36acbSWarner Losh return error; 210912e36acbSWarner Losh } 211012e36acbSWarner Losh 211112e36acbSWarner Losh error = bus_dmamem_alloc(st->stats_ring_dtag, &st->stats_ring, 211212e36acbSWarner Losh BUS_DMA_WAITOK | BUS_DMA_ZERO, 211312e36acbSWarner Losh &st->stats_ring_dmap); 211412e36acbSWarner Losh if (error) { 211512e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate txstats ring " 211612e36acbSWarner Losh "DMA mem\n"); 211712e36acbSWarner Losh bus_dma_tag_destroy(st->stats_ring_dtag); 211812e36acbSWarner Losh st->stats_ring_dtag = NULL; 211912e36acbSWarner Losh return error; 212012e36acbSWarner Losh } 212112e36acbSWarner Losh 212212e36acbSWarner Losh error = bus_dmamap_load(st->stats_ring_dtag, st->stats_ring_dmap, 212312e36acbSWarner Losh st->stats_ring, dma_size, 212412e36acbSWarner Losh bwi_dma_ring_addr, &st->stats_ring_paddr, 212512e36acbSWarner Losh BUS_DMA_NOWAIT); 212612e36acbSWarner Losh if (error) { 212712e36acbSWarner Losh device_printf(sc->sc_dev, "can't load txstats ring DMA mem\n"); 212812e36acbSWarner Losh bus_dmamem_free(st->stats_ring_dtag, st->stats_ring, 212912e36acbSWarner Losh st->stats_ring_dmap); 213012e36acbSWarner Losh bus_dma_tag_destroy(st->stats_ring_dtag); 213112e36acbSWarner Losh st->stats_ring_dtag = NULL; 213212e36acbSWarner Losh return error; 213312e36acbSWarner Losh } 213412e36acbSWarner Losh 213512e36acbSWarner Losh /* 213612e36acbSWarner Losh * Create TX stats DMA stuffs 213712e36acbSWarner Losh */ 213812e36acbSWarner Losh dma_size = roundup(sizeof(struct bwi_txstats) * BWI_TXSTATS_NDESC, 213912e36acbSWarner Losh BWI_ALIGN); 214012e36acbSWarner Losh 214112e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 214212e36acbSWarner Losh BWI_ALIGN, 214312e36acbSWarner Losh 0, 214412e36acbSWarner Losh BUS_SPACE_MAXADDR, 214512e36acbSWarner Losh BUS_SPACE_MAXADDR, 214612e36acbSWarner Losh NULL, NULL, 214712e36acbSWarner Losh dma_size, 214812e36acbSWarner Losh 1, 214912e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, 215012e36acbSWarner Losh BUS_DMA_ALLOCNOW, 215112e36acbSWarner Losh NULL, NULL, 215212e36acbSWarner Losh &st->stats_dtag); 215312e36acbSWarner Losh if (error) { 215412e36acbSWarner Losh device_printf(sc->sc_dev, "can't create txstats DMA tag\n"); 215512e36acbSWarner Losh return error; 215612e36acbSWarner Losh } 215712e36acbSWarner Losh 215812e36acbSWarner Losh error = bus_dmamem_alloc(st->stats_dtag, (void **)&st->stats, 215912e36acbSWarner Losh BUS_DMA_WAITOK | BUS_DMA_ZERO, 216012e36acbSWarner Losh &st->stats_dmap); 216112e36acbSWarner Losh if (error) { 216212e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate txstats DMA mem\n"); 216312e36acbSWarner Losh bus_dma_tag_destroy(st->stats_dtag); 216412e36acbSWarner Losh st->stats_dtag = NULL; 216512e36acbSWarner Losh return error; 216612e36acbSWarner Losh } 216712e36acbSWarner Losh 216812e36acbSWarner Losh error = bus_dmamap_load(st->stats_dtag, st->stats_dmap, st->stats, 216912e36acbSWarner Losh dma_size, bwi_dma_ring_addr, &st->stats_paddr, 217012e36acbSWarner Losh BUS_DMA_NOWAIT); 217112e36acbSWarner Losh if (error) { 217212e36acbSWarner Losh device_printf(sc->sc_dev, "can't load txstats DMA mem\n"); 217312e36acbSWarner Losh bus_dmamem_free(st->stats_dtag, st->stats, st->stats_dmap); 217412e36acbSWarner Losh bus_dma_tag_destroy(st->stats_dtag); 217512e36acbSWarner Losh st->stats_dtag = NULL; 217612e36acbSWarner Losh return error; 217712e36acbSWarner Losh } 217812e36acbSWarner Losh 217912e36acbSWarner Losh st->stats_ctrl_base = ctrl_base; 218012e36acbSWarner Losh return 0; 218112e36acbSWarner Losh } 218212e36acbSWarner Losh 218312e36acbSWarner Losh static void 218412e36acbSWarner Losh bwi_dma_txstats_free(struct bwi_softc *sc) 218512e36acbSWarner Losh { 218612e36acbSWarner Losh struct bwi_txstats_data *st; 218712e36acbSWarner Losh 218812e36acbSWarner Losh if (sc->sc_txstats == NULL) 218912e36acbSWarner Losh return; 219012e36acbSWarner Losh st = sc->sc_txstats; 219112e36acbSWarner Losh 219212e36acbSWarner Losh if (st->stats_ring_dtag != NULL) { 219312e36acbSWarner Losh bus_dmamap_unload(st->stats_ring_dtag, st->stats_ring_dmap); 219412e36acbSWarner Losh bus_dmamem_free(st->stats_ring_dtag, st->stats_ring, 219512e36acbSWarner Losh st->stats_ring_dmap); 219612e36acbSWarner Losh bus_dma_tag_destroy(st->stats_ring_dtag); 219712e36acbSWarner Losh } 219812e36acbSWarner Losh 219912e36acbSWarner Losh if (st->stats_dtag != NULL) { 220012e36acbSWarner Losh bus_dmamap_unload(st->stats_dtag, st->stats_dmap); 220112e36acbSWarner Losh bus_dmamem_free(st->stats_dtag, st->stats, st->stats_dmap); 220212e36acbSWarner Losh bus_dma_tag_destroy(st->stats_dtag); 220312e36acbSWarner Losh } 220412e36acbSWarner Losh 220512e36acbSWarner Losh free(st, M_DEVBUF); 220612e36acbSWarner Losh } 220712e36acbSWarner Losh 220812e36acbSWarner Losh static void 220912e36acbSWarner Losh bwi_dma_ring_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error) 221012e36acbSWarner Losh { 221112e36acbSWarner Losh KASSERT(nseg == 1, ("too many segments\n")); 221212e36acbSWarner Losh *((bus_addr_t *)arg) = seg->ds_addr; 221312e36acbSWarner Losh } 221412e36acbSWarner Losh 221512e36acbSWarner Losh static int 221612e36acbSWarner Losh bwi_dma_mbuf_create(struct bwi_softc *sc) 221712e36acbSWarner Losh { 221812e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 221912e36acbSWarner Losh int i, j, k, ntx, error; 222012e36acbSWarner Losh 222112e36acbSWarner Losh /* 222212e36acbSWarner Losh * Create TX/RX mbuf DMA tag 222312e36acbSWarner Losh */ 222412e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 222512e36acbSWarner Losh 1, 222612e36acbSWarner Losh 0, 222712e36acbSWarner Losh BUS_SPACE_MAXADDR, 222812e36acbSWarner Losh BUS_SPACE_MAXADDR, 222912e36acbSWarner Losh NULL, NULL, 223012e36acbSWarner Losh MCLBYTES, 223112e36acbSWarner Losh 1, 223212e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, 223312e36acbSWarner Losh BUS_DMA_ALLOCNOW, 223412e36acbSWarner Losh NULL, NULL, 223512e36acbSWarner Losh &sc->sc_buf_dtag); 223612e36acbSWarner Losh if (error) { 223712e36acbSWarner Losh device_printf(sc->sc_dev, "can't create mbuf DMA tag\n"); 223812e36acbSWarner Losh return error; 223912e36acbSWarner Losh } 224012e36acbSWarner Losh 224112e36acbSWarner Losh ntx = 0; 224212e36acbSWarner Losh 224312e36acbSWarner Losh /* 224412e36acbSWarner Losh * Create TX mbuf DMA map 224512e36acbSWarner Losh */ 224612e36acbSWarner Losh for (i = 0; i < BWI_TX_NRING; ++i) { 224712e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; 224812e36acbSWarner Losh 224912e36acbSWarner Losh for (j = 0; j < BWI_TX_NDESC; ++j) { 225012e36acbSWarner Losh error = bus_dmamap_create(sc->sc_buf_dtag, 0, 225112e36acbSWarner Losh &tbd->tbd_buf[j].tb_dmap); 225212e36acbSWarner Losh if (error) { 225312e36acbSWarner Losh device_printf(sc->sc_dev, "can't create " 225412e36acbSWarner Losh "%dth tbd, %dth DMA map\n", i, j); 225512e36acbSWarner Losh 225612e36acbSWarner Losh ntx = i; 225712e36acbSWarner Losh for (k = 0; k < j; ++k) { 225812e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, 225912e36acbSWarner Losh tbd->tbd_buf[k].tb_dmap); 226012e36acbSWarner Losh } 226112e36acbSWarner Losh goto fail; 226212e36acbSWarner Losh } 226312e36acbSWarner Losh } 226412e36acbSWarner Losh } 226512e36acbSWarner Losh ntx = BWI_TX_NRING; 226612e36acbSWarner Losh 226712e36acbSWarner Losh /* 226812e36acbSWarner Losh * Create RX mbuf DMA map and a spare DMA map 226912e36acbSWarner Losh */ 227012e36acbSWarner Losh error = bus_dmamap_create(sc->sc_buf_dtag, 0, 227112e36acbSWarner Losh &rbd->rbd_tmp_dmap); 227212e36acbSWarner Losh if (error) { 227312e36acbSWarner Losh device_printf(sc->sc_dev, 227412e36acbSWarner Losh "can't create spare RX buf DMA map\n"); 227512e36acbSWarner Losh goto fail; 227612e36acbSWarner Losh } 227712e36acbSWarner Losh 227812e36acbSWarner Losh for (j = 0; j < BWI_RX_NDESC; ++j) { 227912e36acbSWarner Losh error = bus_dmamap_create(sc->sc_buf_dtag, 0, 228012e36acbSWarner Losh &rbd->rbd_buf[j].rb_dmap); 228112e36acbSWarner Losh if (error) { 228212e36acbSWarner Losh device_printf(sc->sc_dev, "can't create %dth " 228312e36acbSWarner Losh "RX buf DMA map\n", j); 228412e36acbSWarner Losh 228512e36acbSWarner Losh for (k = 0; k < j; ++k) { 228612e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, 228712e36acbSWarner Losh rbd->rbd_buf[j].rb_dmap); 228812e36acbSWarner Losh } 228912e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, 229012e36acbSWarner Losh rbd->rbd_tmp_dmap); 229112e36acbSWarner Losh goto fail; 229212e36acbSWarner Losh } 229312e36acbSWarner Losh } 229412e36acbSWarner Losh 229512e36acbSWarner Losh return 0; 229612e36acbSWarner Losh fail: 229712e36acbSWarner Losh bwi_dma_mbuf_destroy(sc, ntx, 0); 229812e36acbSWarner Losh return error; 229912e36acbSWarner Losh } 230012e36acbSWarner Losh 230112e36acbSWarner Losh static void 230212e36acbSWarner Losh bwi_dma_mbuf_destroy(struct bwi_softc *sc, int ntx, int nrx) 230312e36acbSWarner Losh { 230412e36acbSWarner Losh int i, j; 230512e36acbSWarner Losh 230612e36acbSWarner Losh if (sc->sc_buf_dtag == NULL) 230712e36acbSWarner Losh return; 230812e36acbSWarner Losh 230912e36acbSWarner Losh for (i = 0; i < ntx; ++i) { 231012e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; 231112e36acbSWarner Losh 231212e36acbSWarner Losh for (j = 0; j < BWI_TX_NDESC; ++j) { 231312e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[j]; 231412e36acbSWarner Losh 231512e36acbSWarner Losh if (tb->tb_mbuf != NULL) { 231612e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, 231712e36acbSWarner Losh tb->tb_dmap); 231812e36acbSWarner Losh m_freem(tb->tb_mbuf); 231912e36acbSWarner Losh } 232012e36acbSWarner Losh if (tb->tb_ni != NULL) 232112e36acbSWarner Losh ieee80211_free_node(tb->tb_ni); 232212e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, tb->tb_dmap); 232312e36acbSWarner Losh } 232412e36acbSWarner Losh } 232512e36acbSWarner Losh 232612e36acbSWarner Losh if (nrx) { 232712e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 232812e36acbSWarner Losh 232912e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, rbd->rbd_tmp_dmap); 233012e36acbSWarner Losh for (j = 0; j < BWI_RX_NDESC; ++j) { 233112e36acbSWarner Losh struct bwi_rxbuf *rb = &rbd->rbd_buf[j]; 233212e36acbSWarner Losh 233312e36acbSWarner Losh if (rb->rb_mbuf != NULL) { 233412e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, 233512e36acbSWarner Losh rb->rb_dmap); 233612e36acbSWarner Losh m_freem(rb->rb_mbuf); 233712e36acbSWarner Losh } 233812e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, rb->rb_dmap); 233912e36acbSWarner Losh } 234012e36acbSWarner Losh } 234112e36acbSWarner Losh 234212e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_buf_dtag); 234312e36acbSWarner Losh sc->sc_buf_dtag = NULL; 234412e36acbSWarner Losh } 234512e36acbSWarner Losh 234612e36acbSWarner Losh static void 234712e36acbSWarner Losh bwi_enable_intrs(struct bwi_softc *sc, uint32_t enable_intrs) 234812e36acbSWarner Losh { 234912e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_MAC_INTR_MASK, enable_intrs); 235012e36acbSWarner Losh } 235112e36acbSWarner Losh 235212e36acbSWarner Losh static void 235312e36acbSWarner Losh bwi_disable_intrs(struct bwi_softc *sc, uint32_t disable_intrs) 235412e36acbSWarner Losh { 235512e36acbSWarner Losh CSR_CLRBITS_4(sc, BWI_MAC_INTR_MASK, disable_intrs); 235612e36acbSWarner Losh } 235712e36acbSWarner Losh 235812e36acbSWarner Losh static int 235912e36acbSWarner Losh bwi_init_tx_ring32(struct bwi_softc *sc, int ring_idx) 236012e36acbSWarner Losh { 236112e36acbSWarner Losh struct bwi_ring_data *rd; 236212e36acbSWarner Losh struct bwi_txbuf_data *tbd; 236312e36acbSWarner Losh uint32_t val, addr_hi, addr_lo; 236412e36acbSWarner Losh 236512e36acbSWarner Losh KASSERT(ring_idx < BWI_TX_NRING, ("ring_idx %d", ring_idx)); 236612e36acbSWarner Losh rd = &sc->sc_tx_rdata[ring_idx]; 236712e36acbSWarner Losh tbd = &sc->sc_tx_bdata[ring_idx]; 236812e36acbSWarner Losh 236912e36acbSWarner Losh tbd->tbd_idx = 0; 237012e36acbSWarner Losh tbd->tbd_used = 0; 237112e36acbSWarner Losh 237212e36acbSWarner Losh bzero(rd->rdata_desc, sizeof(struct bwi_desc32) * BWI_TX_NDESC); 237312e36acbSWarner Losh bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, 237412e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 237512e36acbSWarner Losh 237612e36acbSWarner Losh addr_lo = __SHIFTOUT(rd->rdata_paddr, BWI_TXRX32_RINGINFO_ADDR_MASK); 237712e36acbSWarner Losh addr_hi = __SHIFTOUT(rd->rdata_paddr, BWI_TXRX32_RINGINFO_FUNC_MASK); 237812e36acbSWarner Losh 237912e36acbSWarner Losh val = __SHIFTIN(addr_lo, BWI_TXRX32_RINGINFO_ADDR_MASK) | 238012e36acbSWarner Losh __SHIFTIN(BWI_TXRX32_RINGINFO_FUNC_TXRX, 238112e36acbSWarner Losh BWI_TXRX32_RINGINFO_FUNC_MASK); 238212e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_RINGINFO, val); 238312e36acbSWarner Losh 238412e36acbSWarner Losh val = __SHIFTIN(addr_hi, BWI_TXRX32_CTRL_ADDRHI_MASK) | 238512e36acbSWarner Losh BWI_TXRX32_CTRL_ENABLE; 238612e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_CTRL, val); 238712e36acbSWarner Losh 238812e36acbSWarner Losh return 0; 238912e36acbSWarner Losh } 239012e36acbSWarner Losh 239112e36acbSWarner Losh static void 239212e36acbSWarner Losh bwi_init_rxdesc_ring32(struct bwi_softc *sc, uint32_t ctrl_base, 239312e36acbSWarner Losh bus_addr_t paddr, int hdr_size, int ndesc) 239412e36acbSWarner Losh { 239512e36acbSWarner Losh uint32_t val, addr_hi, addr_lo; 239612e36acbSWarner Losh 239712e36acbSWarner Losh addr_lo = __SHIFTOUT(paddr, BWI_TXRX32_RINGINFO_ADDR_MASK); 239812e36acbSWarner Losh addr_hi = __SHIFTOUT(paddr, BWI_TXRX32_RINGINFO_FUNC_MASK); 239912e36acbSWarner Losh 240012e36acbSWarner Losh val = __SHIFTIN(addr_lo, BWI_TXRX32_RINGINFO_ADDR_MASK) | 240112e36acbSWarner Losh __SHIFTIN(BWI_TXRX32_RINGINFO_FUNC_TXRX, 240212e36acbSWarner Losh BWI_TXRX32_RINGINFO_FUNC_MASK); 240312e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_RINGINFO, val); 240412e36acbSWarner Losh 240512e36acbSWarner Losh val = __SHIFTIN(hdr_size, BWI_RX32_CTRL_HDRSZ_MASK) | 240612e36acbSWarner Losh __SHIFTIN(addr_hi, BWI_TXRX32_CTRL_ADDRHI_MASK) | 240712e36acbSWarner Losh BWI_TXRX32_CTRL_ENABLE; 240812e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_CTRL, val); 240912e36acbSWarner Losh 241012e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_INDEX, 241112e36acbSWarner Losh (ndesc - 1) * sizeof(struct bwi_desc32)); 241212e36acbSWarner Losh } 241312e36acbSWarner Losh 241412e36acbSWarner Losh static int 241512e36acbSWarner Losh bwi_init_rx_ring32(struct bwi_softc *sc) 241612e36acbSWarner Losh { 241712e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 241812e36acbSWarner Losh int i, error; 241912e36acbSWarner Losh 242012e36acbSWarner Losh sc->sc_rx_bdata.rbd_idx = 0; 242112e36acbSWarner Losh 242212e36acbSWarner Losh for (i = 0; i < BWI_RX_NDESC; ++i) { 242312e36acbSWarner Losh error = bwi_newbuf(sc, i, 1); 242412e36acbSWarner Losh if (error) { 242512e36acbSWarner Losh device_printf(sc->sc_dev, 242612e36acbSWarner Losh "can't allocate %dth RX buffer\n", i); 242712e36acbSWarner Losh return error; 242812e36acbSWarner Losh } 242912e36acbSWarner Losh } 243012e36acbSWarner Losh bus_dmamap_sync(sc->sc_rxring_dtag, rd->rdata_dmap, 243112e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 243212e36acbSWarner Losh 243312e36acbSWarner Losh bwi_init_rxdesc_ring32(sc, rd->rdata_txrx_ctrl, rd->rdata_paddr, 243412e36acbSWarner Losh sizeof(struct bwi_rxbuf_hdr), BWI_RX_NDESC); 243512e36acbSWarner Losh return 0; 243612e36acbSWarner Losh } 243712e36acbSWarner Losh 243812e36acbSWarner Losh static int 243912e36acbSWarner Losh bwi_init_txstats32(struct bwi_softc *sc) 244012e36acbSWarner Losh { 244112e36acbSWarner Losh struct bwi_txstats_data *st = sc->sc_txstats; 244212e36acbSWarner Losh bus_addr_t stats_paddr; 244312e36acbSWarner Losh int i; 244412e36acbSWarner Losh 244512e36acbSWarner Losh bzero(st->stats, BWI_TXSTATS_NDESC * sizeof(struct bwi_txstats)); 244612e36acbSWarner Losh bus_dmamap_sync(st->stats_dtag, st->stats_dmap, BUS_DMASYNC_PREWRITE); 244712e36acbSWarner Losh 244812e36acbSWarner Losh st->stats_idx = 0; 244912e36acbSWarner Losh 245012e36acbSWarner Losh stats_paddr = st->stats_paddr; 245112e36acbSWarner Losh for (i = 0; i < BWI_TXSTATS_NDESC; ++i) { 245212e36acbSWarner Losh bwi_setup_desc32(sc, st->stats_ring, BWI_TXSTATS_NDESC, i, 245312e36acbSWarner Losh stats_paddr, sizeof(struct bwi_txstats), 0); 245412e36acbSWarner Losh stats_paddr += sizeof(struct bwi_txstats); 245512e36acbSWarner Losh } 245612e36acbSWarner Losh bus_dmamap_sync(st->stats_ring_dtag, st->stats_ring_dmap, 245712e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 245812e36acbSWarner Losh 245912e36acbSWarner Losh bwi_init_rxdesc_ring32(sc, st->stats_ctrl_base, 246012e36acbSWarner Losh st->stats_ring_paddr, 0, BWI_TXSTATS_NDESC); 246112e36acbSWarner Losh return 0; 246212e36acbSWarner Losh } 246312e36acbSWarner Losh 246412e36acbSWarner Losh static void 246512e36acbSWarner Losh bwi_setup_rx_desc32(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, 246612e36acbSWarner Losh int buf_len) 246712e36acbSWarner Losh { 246812e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 246912e36acbSWarner Losh 247012e36acbSWarner Losh KASSERT(buf_idx < BWI_RX_NDESC, ("buf_idx %d", buf_idx)); 247112e36acbSWarner Losh bwi_setup_desc32(sc, rd->rdata_desc, BWI_RX_NDESC, buf_idx, 247212e36acbSWarner Losh paddr, buf_len, 0); 247312e36acbSWarner Losh } 247412e36acbSWarner Losh 247512e36acbSWarner Losh static void 247612e36acbSWarner Losh bwi_setup_tx_desc32(struct bwi_softc *sc, struct bwi_ring_data *rd, 247712e36acbSWarner Losh int buf_idx, bus_addr_t paddr, int buf_len) 247812e36acbSWarner Losh { 247912e36acbSWarner Losh KASSERT(buf_idx < BWI_TX_NDESC, ("buf_idx %d", buf_idx)); 248012e36acbSWarner Losh bwi_setup_desc32(sc, rd->rdata_desc, BWI_TX_NDESC, buf_idx, 248112e36acbSWarner Losh paddr, buf_len, 1); 248212e36acbSWarner Losh } 248312e36acbSWarner Losh 248412e36acbSWarner Losh static int 248512e36acbSWarner Losh bwi_init_tx_ring64(struct bwi_softc *sc, int ring_idx) 248612e36acbSWarner Losh { 248712e36acbSWarner Losh /* TODO:64 */ 248812e36acbSWarner Losh return EOPNOTSUPP; 248912e36acbSWarner Losh } 249012e36acbSWarner Losh 249112e36acbSWarner Losh static int 249212e36acbSWarner Losh bwi_init_rx_ring64(struct bwi_softc *sc) 249312e36acbSWarner Losh { 249412e36acbSWarner Losh /* TODO:64 */ 249512e36acbSWarner Losh return EOPNOTSUPP; 249612e36acbSWarner Losh } 249712e36acbSWarner Losh 249812e36acbSWarner Losh static int 249912e36acbSWarner Losh bwi_init_txstats64(struct bwi_softc *sc) 250012e36acbSWarner Losh { 250112e36acbSWarner Losh /* TODO:64 */ 250212e36acbSWarner Losh return EOPNOTSUPP; 250312e36acbSWarner Losh } 250412e36acbSWarner Losh 250512e36acbSWarner Losh static void 250612e36acbSWarner Losh bwi_setup_rx_desc64(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, 250712e36acbSWarner Losh int buf_len) 250812e36acbSWarner Losh { 250912e36acbSWarner Losh /* TODO:64 */ 251012e36acbSWarner Losh } 251112e36acbSWarner Losh 251212e36acbSWarner Losh static void 251312e36acbSWarner Losh bwi_setup_tx_desc64(struct bwi_softc *sc, struct bwi_ring_data *rd, 251412e36acbSWarner Losh int buf_idx, bus_addr_t paddr, int buf_len) 251512e36acbSWarner Losh { 251612e36acbSWarner Losh /* TODO:64 */ 251712e36acbSWarner Losh } 251812e36acbSWarner Losh 251912e36acbSWarner Losh static void 252012e36acbSWarner Losh bwi_dma_buf_addr(void *arg, bus_dma_segment_t *seg, int nseg, 252112e36acbSWarner Losh bus_size_t mapsz __unused, int error) 252212e36acbSWarner Losh { 252312e36acbSWarner Losh if (!error) { 252412e36acbSWarner Losh KASSERT(nseg == 1, ("too many segments(%d)\n", nseg)); 252512e36acbSWarner Losh *((bus_addr_t *)arg) = seg->ds_addr; 252612e36acbSWarner Losh } 252712e36acbSWarner Losh } 252812e36acbSWarner Losh 252912e36acbSWarner Losh static int 253012e36acbSWarner Losh bwi_newbuf(struct bwi_softc *sc, int buf_idx, int init) 253112e36acbSWarner Losh { 253212e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 253312e36acbSWarner Losh struct bwi_rxbuf *rxbuf = &rbd->rbd_buf[buf_idx]; 253412e36acbSWarner Losh struct bwi_rxbuf_hdr *hdr; 253512e36acbSWarner Losh bus_dmamap_t map; 253612e36acbSWarner Losh bus_addr_t paddr; 253712e36acbSWarner Losh struct mbuf *m; 253812e36acbSWarner Losh int error; 253912e36acbSWarner Losh 254012e36acbSWarner Losh KASSERT(buf_idx < BWI_RX_NDESC, ("buf_idx %d", buf_idx)); 254112e36acbSWarner Losh 254212e36acbSWarner Losh m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 254312e36acbSWarner Losh if (m == NULL) { 254412e36acbSWarner Losh error = ENOBUFS; 254512e36acbSWarner Losh 254612e36acbSWarner Losh /* 254712e36acbSWarner Losh * If the NIC is up and running, we need to: 254812e36acbSWarner Losh * - Clear RX buffer's header. 254912e36acbSWarner Losh * - Restore RX descriptor settings. 255012e36acbSWarner Losh */ 255112e36acbSWarner Losh if (init) 255212e36acbSWarner Losh return error; 255312e36acbSWarner Losh else 255412e36acbSWarner Losh goto back; 255512e36acbSWarner Losh } 255612e36acbSWarner Losh m->m_len = m->m_pkthdr.len = MCLBYTES; 255712e36acbSWarner Losh 255812e36acbSWarner Losh /* 255912e36acbSWarner Losh * Try to load RX buf into temporary DMA map 256012e36acbSWarner Losh */ 256112e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, rbd->rbd_tmp_dmap, m, 256212e36acbSWarner Losh bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); 256312e36acbSWarner Losh if (error) { 256412e36acbSWarner Losh m_freem(m); 256512e36acbSWarner Losh 256612e36acbSWarner Losh /* 256712e36acbSWarner Losh * See the comment above 256812e36acbSWarner Losh */ 256912e36acbSWarner Losh if (init) 257012e36acbSWarner Losh return error; 257112e36acbSWarner Losh else 257212e36acbSWarner Losh goto back; 257312e36acbSWarner Losh } 257412e36acbSWarner Losh 257512e36acbSWarner Losh if (!init) 257612e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, rxbuf->rb_dmap); 257712e36acbSWarner Losh rxbuf->rb_mbuf = m; 257812e36acbSWarner Losh rxbuf->rb_paddr = paddr; 257912e36acbSWarner Losh 258012e36acbSWarner Losh /* 258112e36acbSWarner Losh * Swap RX buf's DMA map with the loaded temporary one 258212e36acbSWarner Losh */ 258312e36acbSWarner Losh map = rxbuf->rb_dmap; 258412e36acbSWarner Losh rxbuf->rb_dmap = rbd->rbd_tmp_dmap; 258512e36acbSWarner Losh rbd->rbd_tmp_dmap = map; 258612e36acbSWarner Losh 258712e36acbSWarner Losh back: 258812e36acbSWarner Losh /* 258912e36acbSWarner Losh * Clear RX buf header 259012e36acbSWarner Losh */ 259112e36acbSWarner Losh hdr = mtod(rxbuf->rb_mbuf, struct bwi_rxbuf_hdr *); 259212e36acbSWarner Losh bzero(hdr, sizeof(*hdr)); 259312e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, rxbuf->rb_dmap, BUS_DMASYNC_PREWRITE); 259412e36acbSWarner Losh 259512e36acbSWarner Losh /* 259612e36acbSWarner Losh * Setup RX buf descriptor 259712e36acbSWarner Losh */ 259812e36acbSWarner Losh sc->sc_setup_rxdesc(sc, buf_idx, rxbuf->rb_paddr, 259912e36acbSWarner Losh rxbuf->rb_mbuf->m_len - sizeof(*hdr)); 260012e36acbSWarner Losh return error; 260112e36acbSWarner Losh } 260212e36acbSWarner Losh 260312e36acbSWarner Losh static void 260412e36acbSWarner Losh bwi_set_addr_filter(struct bwi_softc *sc, uint16_t addr_ofs, 260512e36acbSWarner Losh const uint8_t *addr) 260612e36acbSWarner Losh { 260712e36acbSWarner Losh int i; 260812e36acbSWarner Losh 260912e36acbSWarner Losh CSR_WRITE_2(sc, BWI_ADDR_FILTER_CTRL, 261012e36acbSWarner Losh BWI_ADDR_FILTER_CTRL_SET | addr_ofs); 261112e36acbSWarner Losh 261212e36acbSWarner Losh for (i = 0; i < (IEEE80211_ADDR_LEN / 2); ++i) { 261312e36acbSWarner Losh uint16_t addr_val; 261412e36acbSWarner Losh 261512e36acbSWarner Losh addr_val = (uint16_t)addr[i * 2] | 261612e36acbSWarner Losh (((uint16_t)addr[(i * 2) + 1]) << 8); 261712e36acbSWarner Losh CSR_WRITE_2(sc, BWI_ADDR_FILTER_DATA, addr_val); 261812e36acbSWarner Losh } 261912e36acbSWarner Losh } 262012e36acbSWarner Losh 262112e36acbSWarner Losh static int 262212e36acbSWarner Losh bwi_rxeof(struct bwi_softc *sc, int end_idx) 262312e36acbSWarner Losh { 262412e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 262512e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 262612e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 262712e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 262812e36acbSWarner Losh int idx, rx_data = 0; 262912e36acbSWarner Losh 263012e36acbSWarner Losh idx = rbd->rbd_idx; 263112e36acbSWarner Losh while (idx != end_idx) { 263212e36acbSWarner Losh struct bwi_rxbuf *rb = &rbd->rbd_buf[idx]; 263312e36acbSWarner Losh struct bwi_rxbuf_hdr *hdr; 263412e36acbSWarner Losh struct ieee80211_frame_min *wh; 263512e36acbSWarner Losh struct ieee80211_node *ni; 263612e36acbSWarner Losh struct mbuf *m; 263712e36acbSWarner Losh const void *plcp; 263812e36acbSWarner Losh uint16_t flags2; 263912e36acbSWarner Losh int buflen, wh_ofs, hdr_extra, rssi, noise, type, rate; 264012e36acbSWarner Losh 264112e36acbSWarner Losh m = rb->rb_mbuf; 264212e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, rb->rb_dmap, 264312e36acbSWarner Losh BUS_DMASYNC_POSTREAD); 264412e36acbSWarner Losh 264512e36acbSWarner Losh if (bwi_newbuf(sc, idx, 0)) { 264612e36acbSWarner Losh ifp->if_ierrors++; 264712e36acbSWarner Losh goto next; 264812e36acbSWarner Losh } 264912e36acbSWarner Losh 265012e36acbSWarner Losh hdr = mtod(m, struct bwi_rxbuf_hdr *); 265112e36acbSWarner Losh flags2 = le16toh(hdr->rxh_flags2); 265212e36acbSWarner Losh 265312e36acbSWarner Losh hdr_extra = 0; 265412e36acbSWarner Losh if (flags2 & BWI_RXH_F2_TYPE2FRAME) 265512e36acbSWarner Losh hdr_extra = 2; 265612e36acbSWarner Losh wh_ofs = hdr_extra + 6; /* XXX magic number */ 265712e36acbSWarner Losh 265812e36acbSWarner Losh buflen = le16toh(hdr->rxh_buflen); 265912e36acbSWarner Losh if (buflen < BWI_FRAME_MIN_LEN(wh_ofs)) { 266012e36acbSWarner Losh if_printf(ifp, "%s: zero length data, hdr_extra %d\n", 266112e36acbSWarner Losh __func__, hdr_extra); 266212e36acbSWarner Losh ifp->if_ierrors++; 266312e36acbSWarner Losh m_freem(m); 266412e36acbSWarner Losh goto next; 266512e36acbSWarner Losh } 266612e36acbSWarner Losh 266712e36acbSWarner Losh plcp = ((const uint8_t *)(hdr + 1) + hdr_extra); 266812e36acbSWarner Losh rssi = bwi_calc_rssi(sc, hdr); 266912e36acbSWarner Losh noise = bwi_calc_noise(sc); 267012e36acbSWarner Losh 267112e36acbSWarner Losh m->m_pkthdr.rcvif = ifp; 267212e36acbSWarner Losh m->m_len = m->m_pkthdr.len = buflen + sizeof(*hdr); 267312e36acbSWarner Losh m_adj(m, sizeof(*hdr) + wh_ofs); 267412e36acbSWarner Losh 267512e36acbSWarner Losh if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_OFDM) 267612e36acbSWarner Losh rate = bwi_ofdm_plcp2rate(plcp); 267712e36acbSWarner Losh else 267812e36acbSWarner Losh rate = bwi_ds_plcp2rate(plcp); 267912e36acbSWarner Losh 268012e36acbSWarner Losh /* RX radio tap */ 26815463c4a4SSam Leffler if (ieee80211_radiotap_active(ic)) 26825463c4a4SSam Leffler bwi_rx_radiotap(sc, m, hdr, plcp, rate, rssi, noise); 268312e36acbSWarner Losh 268412e36acbSWarner Losh m_adj(m, -IEEE80211_CRC_LEN); 268512e36acbSWarner Losh 268612e36acbSWarner Losh BWI_UNLOCK(sc); 268712e36acbSWarner Losh 268812e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame_min *); 268912e36acbSWarner Losh ni = ieee80211_find_rxnode(ic, wh); 269012e36acbSWarner Losh if (ni != NULL) { 26915463c4a4SSam Leffler type = ieee80211_input(ni, m, rssi - noise, noise); 269212e36acbSWarner Losh ieee80211_free_node(ni); 269312e36acbSWarner Losh } else 26945463c4a4SSam Leffler type = ieee80211_input_all(ic, m, rssi - noise, noise); 269512e36acbSWarner Losh if (type == IEEE80211_FC0_TYPE_DATA) { 269612e36acbSWarner Losh rx_data = 1; 269712e36acbSWarner Losh sc->sc_rx_rate = rate; 269812e36acbSWarner Losh } 269912e36acbSWarner Losh 270012e36acbSWarner Losh BWI_LOCK(sc); 270112e36acbSWarner Losh next: 270212e36acbSWarner Losh idx = (idx + 1) % BWI_RX_NDESC; 270312e36acbSWarner Losh 270412e36acbSWarner Losh if (sc->sc_flags & BWI_F_STOP) { 270512e36acbSWarner Losh /* 270612e36acbSWarner Losh * Take the fast lane, don't do 270712e36acbSWarner Losh * any damage to softc 270812e36acbSWarner Losh */ 270912e36acbSWarner Losh return -1; 271012e36acbSWarner Losh } 271112e36acbSWarner Losh } 271212e36acbSWarner Losh 271312e36acbSWarner Losh rbd->rbd_idx = idx; 271412e36acbSWarner Losh bus_dmamap_sync(sc->sc_rxring_dtag, rd->rdata_dmap, 271512e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 271612e36acbSWarner Losh 271712e36acbSWarner Losh return rx_data; 271812e36acbSWarner Losh } 271912e36acbSWarner Losh 272012e36acbSWarner Losh static int 272112e36acbSWarner Losh bwi_rxeof32(struct bwi_softc *sc) 272212e36acbSWarner Losh { 272312e36acbSWarner Losh uint32_t val, rx_ctrl; 272412e36acbSWarner Losh int end_idx, rx_data; 272512e36acbSWarner Losh 272612e36acbSWarner Losh rx_ctrl = sc->sc_rx_rdata.rdata_txrx_ctrl; 272712e36acbSWarner Losh 272812e36acbSWarner Losh val = CSR_READ_4(sc, rx_ctrl + BWI_RX32_STATUS); 272912e36acbSWarner Losh end_idx = __SHIFTOUT(val, BWI_RX32_STATUS_INDEX_MASK) / 273012e36acbSWarner Losh sizeof(struct bwi_desc32); 273112e36acbSWarner Losh 273212e36acbSWarner Losh rx_data = bwi_rxeof(sc, end_idx); 273312e36acbSWarner Losh if (rx_data >= 0) { 273412e36acbSWarner Losh CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_INDEX, 273512e36acbSWarner Losh end_idx * sizeof(struct bwi_desc32)); 273612e36acbSWarner Losh } 273712e36acbSWarner Losh return rx_data; 273812e36acbSWarner Losh } 273912e36acbSWarner Losh 274012e36acbSWarner Losh static int 274112e36acbSWarner Losh bwi_rxeof64(struct bwi_softc *sc) 274212e36acbSWarner Losh { 274312e36acbSWarner Losh /* TODO:64 */ 274412e36acbSWarner Losh return 0; 274512e36acbSWarner Losh } 274612e36acbSWarner Losh 274712e36acbSWarner Losh static void 274812e36acbSWarner Losh bwi_reset_rx_ring32(struct bwi_softc *sc, uint32_t rx_ctrl) 274912e36acbSWarner Losh { 275012e36acbSWarner Losh int i; 275112e36acbSWarner Losh 275212e36acbSWarner Losh CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_CTRL, 0); 275312e36acbSWarner Losh 275412e36acbSWarner Losh #define NRETRY 10 275512e36acbSWarner Losh 275612e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 275712e36acbSWarner Losh uint32_t status; 275812e36acbSWarner Losh 275912e36acbSWarner Losh status = CSR_READ_4(sc, rx_ctrl + BWI_RX32_STATUS); 276012e36acbSWarner Losh if (__SHIFTOUT(status, BWI_RX32_STATUS_STATE_MASK) == 276112e36acbSWarner Losh BWI_RX32_STATUS_STATE_DISABLED) 276212e36acbSWarner Losh break; 276312e36acbSWarner Losh 276412e36acbSWarner Losh DELAY(1000); 276512e36acbSWarner Losh } 276612e36acbSWarner Losh if (i == NRETRY) 276712e36acbSWarner Losh device_printf(sc->sc_dev, "reset rx ring timedout\n"); 276812e36acbSWarner Losh 276912e36acbSWarner Losh #undef NRETRY 277012e36acbSWarner Losh 277112e36acbSWarner Losh CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_RINGINFO, 0); 277212e36acbSWarner Losh } 277312e36acbSWarner Losh 277412e36acbSWarner Losh static void 277512e36acbSWarner Losh bwi_free_txstats32(struct bwi_softc *sc) 277612e36acbSWarner Losh { 277712e36acbSWarner Losh bwi_reset_rx_ring32(sc, sc->sc_txstats->stats_ctrl_base); 277812e36acbSWarner Losh } 277912e36acbSWarner Losh 278012e36acbSWarner Losh static void 278112e36acbSWarner Losh bwi_free_rx_ring32(struct bwi_softc *sc) 278212e36acbSWarner Losh { 278312e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 278412e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 278512e36acbSWarner Losh int i; 278612e36acbSWarner Losh 278712e36acbSWarner Losh bwi_reset_rx_ring32(sc, rd->rdata_txrx_ctrl); 278812e36acbSWarner Losh 278912e36acbSWarner Losh for (i = 0; i < BWI_RX_NDESC; ++i) { 279012e36acbSWarner Losh struct bwi_rxbuf *rb = &rbd->rbd_buf[i]; 279112e36acbSWarner Losh 279212e36acbSWarner Losh if (rb->rb_mbuf != NULL) { 279312e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, rb->rb_dmap); 279412e36acbSWarner Losh m_freem(rb->rb_mbuf); 279512e36acbSWarner Losh rb->rb_mbuf = NULL; 279612e36acbSWarner Losh } 279712e36acbSWarner Losh } 279812e36acbSWarner Losh } 279912e36acbSWarner Losh 280012e36acbSWarner Losh static void 280112e36acbSWarner Losh bwi_free_tx_ring32(struct bwi_softc *sc, int ring_idx) 280212e36acbSWarner Losh { 280312e36acbSWarner Losh struct bwi_ring_data *rd; 280412e36acbSWarner Losh struct bwi_txbuf_data *tbd; 280512e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 280612e36acbSWarner Losh uint32_t state, val; 280712e36acbSWarner Losh int i; 280812e36acbSWarner Losh 280912e36acbSWarner Losh KASSERT(ring_idx < BWI_TX_NRING, ("ring_idx %d", ring_idx)); 281012e36acbSWarner Losh rd = &sc->sc_tx_rdata[ring_idx]; 281112e36acbSWarner Losh tbd = &sc->sc_tx_bdata[ring_idx]; 281212e36acbSWarner Losh 281312e36acbSWarner Losh #define NRETRY 10 281412e36acbSWarner Losh 281512e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 281612e36acbSWarner Losh val = CSR_READ_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_STATUS); 281712e36acbSWarner Losh state = __SHIFTOUT(val, BWI_TX32_STATUS_STATE_MASK); 281812e36acbSWarner Losh if (state == BWI_TX32_STATUS_STATE_DISABLED || 281912e36acbSWarner Losh state == BWI_TX32_STATUS_STATE_IDLE || 282012e36acbSWarner Losh state == BWI_TX32_STATUS_STATE_STOPPED) 282112e36acbSWarner Losh break; 282212e36acbSWarner Losh 282312e36acbSWarner Losh DELAY(1000); 282412e36acbSWarner Losh } 282512e36acbSWarner Losh if (i == NRETRY) { 282612e36acbSWarner Losh if_printf(ifp, "%s: wait for TX ring(%d) stable timed out\n", 282712e36acbSWarner Losh __func__, ring_idx); 282812e36acbSWarner Losh } 282912e36acbSWarner Losh 283012e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_CTRL, 0); 283112e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 283212e36acbSWarner Losh val = CSR_READ_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_STATUS); 283312e36acbSWarner Losh state = __SHIFTOUT(val, BWI_TX32_STATUS_STATE_MASK); 283412e36acbSWarner Losh if (state == BWI_TX32_STATUS_STATE_DISABLED) 283512e36acbSWarner Losh break; 283612e36acbSWarner Losh 283712e36acbSWarner Losh DELAY(1000); 283812e36acbSWarner Losh } 283912e36acbSWarner Losh if (i == NRETRY) 284012e36acbSWarner Losh if_printf(ifp, "%s: reset TX ring (%d) timed out\n", 284112e36acbSWarner Losh __func__, ring_idx); 284212e36acbSWarner Losh 284312e36acbSWarner Losh #undef NRETRY 284412e36acbSWarner Losh 284512e36acbSWarner Losh DELAY(1000); 284612e36acbSWarner Losh 284712e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_RINGINFO, 0); 284812e36acbSWarner Losh 284912e36acbSWarner Losh for (i = 0; i < BWI_TX_NDESC; ++i) { 285012e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[i]; 285112e36acbSWarner Losh 285212e36acbSWarner Losh if (tb->tb_mbuf != NULL) { 285312e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); 285412e36acbSWarner Losh m_freem(tb->tb_mbuf); 285512e36acbSWarner Losh tb->tb_mbuf = NULL; 285612e36acbSWarner Losh } 285712e36acbSWarner Losh if (tb->tb_ni != NULL) { 285812e36acbSWarner Losh ieee80211_free_node(tb->tb_ni); 285912e36acbSWarner Losh tb->tb_ni = NULL; 286012e36acbSWarner Losh } 286112e36acbSWarner Losh } 286212e36acbSWarner Losh } 286312e36acbSWarner Losh 286412e36acbSWarner Losh static void 286512e36acbSWarner Losh bwi_free_txstats64(struct bwi_softc *sc) 286612e36acbSWarner Losh { 286712e36acbSWarner Losh /* TODO:64 */ 286812e36acbSWarner Losh } 286912e36acbSWarner Losh 287012e36acbSWarner Losh static void 287112e36acbSWarner Losh bwi_free_rx_ring64(struct bwi_softc *sc) 287212e36acbSWarner Losh { 287312e36acbSWarner Losh /* TODO:64 */ 287412e36acbSWarner Losh } 287512e36acbSWarner Losh 287612e36acbSWarner Losh static void 287712e36acbSWarner Losh bwi_free_tx_ring64(struct bwi_softc *sc, int ring_idx) 287812e36acbSWarner Losh { 287912e36acbSWarner Losh /* TODO:64 */ 288012e36acbSWarner Losh } 288112e36acbSWarner Losh 288212e36acbSWarner Losh /* XXX does not belong here */ 288312e36acbSWarner Losh #define IEEE80211_OFDM_PLCP_RATE_MASK __BITS(3, 0) 288412e36acbSWarner Losh #define IEEE80211_OFDM_PLCP_LEN_MASK __BITS(16, 5) 288512e36acbSWarner Losh 288612e36acbSWarner Losh static __inline void 288712e36acbSWarner Losh bwi_ofdm_plcp_header(uint32_t *plcp0, int pkt_len, uint8_t rate) 288812e36acbSWarner Losh { 288912e36acbSWarner Losh uint32_t plcp; 289012e36acbSWarner Losh 289112e36acbSWarner Losh plcp = __SHIFTIN(ieee80211_rate2plcp(rate, IEEE80211_T_OFDM), 289212e36acbSWarner Losh IEEE80211_OFDM_PLCP_RATE_MASK) | 289312e36acbSWarner Losh __SHIFTIN(pkt_len, IEEE80211_OFDM_PLCP_LEN_MASK); 289412e36acbSWarner Losh *plcp0 = htole32(plcp); 289512e36acbSWarner Losh } 289612e36acbSWarner Losh 289712e36acbSWarner Losh static __inline void 289812e36acbSWarner Losh bwi_ds_plcp_header(struct ieee80211_ds_plcp_hdr *plcp, int pkt_len, 289912e36acbSWarner Losh uint8_t rate) 290012e36acbSWarner Losh { 290112e36acbSWarner Losh int len, service, pkt_bitlen; 290212e36acbSWarner Losh 290312e36acbSWarner Losh pkt_bitlen = pkt_len * NBBY; 290412e36acbSWarner Losh len = howmany(pkt_bitlen * 2, rate); 290512e36acbSWarner Losh 290612e36acbSWarner Losh service = IEEE80211_PLCP_SERVICE_LOCKED; 290712e36acbSWarner Losh if (rate == (11 * 2)) { 290812e36acbSWarner Losh int pkt_bitlen1; 290912e36acbSWarner Losh 291012e36acbSWarner Losh /* 291112e36acbSWarner Losh * PLCP service field needs to be adjusted, 291212e36acbSWarner Losh * if TX rate is 11Mbytes/s 291312e36acbSWarner Losh */ 291412e36acbSWarner Losh pkt_bitlen1 = len * 11; 291512e36acbSWarner Losh if (pkt_bitlen1 - pkt_bitlen >= NBBY) 291612e36acbSWarner Losh service |= IEEE80211_PLCP_SERVICE_LENEXT7; 291712e36acbSWarner Losh } 291812e36acbSWarner Losh 291912e36acbSWarner Losh plcp->i_signal = ieee80211_rate2plcp(rate, IEEE80211_T_CCK); 292012e36acbSWarner Losh plcp->i_service = service; 292112e36acbSWarner Losh plcp->i_length = htole16(len); 292212e36acbSWarner Losh /* NOTE: do NOT touch i_crc */ 292312e36acbSWarner Losh } 292412e36acbSWarner Losh 292512e36acbSWarner Losh static __inline void 292612e36acbSWarner Losh bwi_plcp_header(const struct ieee80211_rate_table *rt, 292712e36acbSWarner Losh void *plcp, int pkt_len, uint8_t rate) 292812e36acbSWarner Losh { 292912e36acbSWarner Losh enum ieee80211_phytype modtype; 293012e36acbSWarner Losh 293112e36acbSWarner Losh /* 293212e36acbSWarner Losh * Assume caller has zeroed 'plcp' 293312e36acbSWarner Losh */ 293412e36acbSWarner Losh modtype = ieee80211_rate2phytype(rt, rate); 293512e36acbSWarner Losh if (modtype == IEEE80211_T_OFDM) 293612e36acbSWarner Losh bwi_ofdm_plcp_header(plcp, pkt_len, rate); 293712e36acbSWarner Losh else if (modtype == IEEE80211_T_DS) 293812e36acbSWarner Losh bwi_ds_plcp_header(plcp, pkt_len, rate); 293912e36acbSWarner Losh else 294012e36acbSWarner Losh panic("unsupport modulation type %u\n", modtype); 294112e36acbSWarner Losh } 294212e36acbSWarner Losh 294312e36acbSWarner Losh static int 294412e36acbSWarner Losh bwi_encap(struct bwi_softc *sc, int idx, struct mbuf *m, 294512e36acbSWarner Losh struct ieee80211_node *ni) 294612e36acbSWarner Losh { 294712e36acbSWarner Losh struct ieee80211vap *vap = ni->ni_vap; 294812e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 294912e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 295012e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_tx_rdata[BWI_TX_DATA_RING]; 295112e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 295212e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; 295312e36acbSWarner Losh struct bwi_mac *mac; 295412e36acbSWarner Losh struct bwi_txbuf_hdr *hdr; 295512e36acbSWarner Losh struct ieee80211_frame *wh; 295612e36acbSWarner Losh const struct ieee80211_txparam *tp; 295712e36acbSWarner Losh uint8_t rate, rate_fb; 295812e36acbSWarner Losh uint32_t mac_ctrl; 295912e36acbSWarner Losh uint16_t phy_ctrl; 296012e36acbSWarner Losh bus_addr_t paddr; 296112e36acbSWarner Losh int type, ismcast, pkt_len, error, rix; 296212e36acbSWarner Losh #if 0 296312e36acbSWarner Losh const uint8_t *p; 296412e36acbSWarner Losh int i; 296512e36acbSWarner Losh #endif 296612e36acbSWarner Losh 296712e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 296812e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 296912e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 297012e36acbSWarner Losh 297112e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame *); 297212e36acbSWarner Losh type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 297312e36acbSWarner Losh ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 297412e36acbSWarner Losh 297512e36acbSWarner Losh /* Get 802.11 frame len before prepending TX header */ 297612e36acbSWarner Losh pkt_len = m->m_pkthdr.len + IEEE80211_CRC_LEN; 297712e36acbSWarner Losh 297812e36acbSWarner Losh /* 297912e36acbSWarner Losh * Find TX rate 298012e36acbSWarner Losh */ 298112e36acbSWarner Losh tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; 298212e36acbSWarner Losh if (type != IEEE80211_FC0_TYPE_DATA || (m->m_flags & M_EAPOL)) { 298312e36acbSWarner Losh rate = rate_fb = tp->mgmtrate; 298412e36acbSWarner Losh } else if (ismcast) { 298512e36acbSWarner Losh rate = rate_fb = tp->mcastrate; 298612e36acbSWarner Losh } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { 298712e36acbSWarner Losh rate = rate_fb = tp->ucastrate; 298812e36acbSWarner Losh } else { 2989b6108616SRui Paulo rix = ieee80211_ratectl_rate(ni, NULL, pkt_len); 299012e36acbSWarner Losh rate = ni->ni_txrate; 299112e36acbSWarner Losh 299212e36acbSWarner Losh if (rix > 0) { 299312e36acbSWarner Losh rate_fb = ni->ni_rates.rs_rates[rix-1] & 299412e36acbSWarner Losh IEEE80211_RATE_VAL; 299512e36acbSWarner Losh } else { 299612e36acbSWarner Losh rate_fb = rate; 299712e36acbSWarner Losh } 299812e36acbSWarner Losh } 299912e36acbSWarner Losh tb->tb_rate[0] = rate; 300012e36acbSWarner Losh tb->tb_rate[1] = rate_fb; 300112e36acbSWarner Losh sc->sc_tx_rate = rate; 300212e36acbSWarner Losh 300312e36acbSWarner Losh /* 300412e36acbSWarner Losh * TX radio tap 300512e36acbSWarner Losh */ 30065463c4a4SSam Leffler if (ieee80211_radiotap_active_vap(vap)) { 300712e36acbSWarner Losh sc->sc_tx_th.wt_flags = 0; 300812e36acbSWarner Losh if (wh->i_fc[1] & IEEE80211_FC1_WEP) 300912e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 301012e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_DS && 301112e36acbSWarner Losh (ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 301212e36acbSWarner Losh rate != (1 * 2)) { 301312e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 301412e36acbSWarner Losh } 301512e36acbSWarner Losh sc->sc_tx_th.wt_rate = rate; 301612e36acbSWarner Losh 30175463c4a4SSam Leffler ieee80211_radiotap_tx(vap, m); 301812e36acbSWarner Losh } 301912e36acbSWarner Losh 302012e36acbSWarner Losh /* 302112e36acbSWarner Losh * Setup the embedded TX header 302212e36acbSWarner Losh */ 302312e36acbSWarner Losh M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); 302412e36acbSWarner Losh if (m == NULL) { 302512e36acbSWarner Losh if_printf(ifp, "%s: prepend TX header failed\n", __func__); 302612e36acbSWarner Losh return ENOBUFS; 302712e36acbSWarner Losh } 302812e36acbSWarner Losh hdr = mtod(m, struct bwi_txbuf_hdr *); 302912e36acbSWarner Losh 303012e36acbSWarner Losh bzero(hdr, sizeof(*hdr)); 303112e36acbSWarner Losh 303212e36acbSWarner Losh bcopy(wh->i_fc, hdr->txh_fc, sizeof(hdr->txh_fc)); 303312e36acbSWarner Losh bcopy(wh->i_addr1, hdr->txh_addr1, sizeof(hdr->txh_addr1)); 303412e36acbSWarner Losh 303512e36acbSWarner Losh if (!ismcast) { 303612e36acbSWarner Losh uint16_t dur; 303712e36acbSWarner Losh 303812e36acbSWarner Losh dur = ieee80211_ack_duration(sc->sc_rates, rate, 303912e36acbSWarner Losh ic->ic_flags & ~IEEE80211_F_SHPREAMBLE); 304012e36acbSWarner Losh 304112e36acbSWarner Losh hdr->txh_fb_duration = htole16(dur); 304212e36acbSWarner Losh } 304312e36acbSWarner Losh 304412e36acbSWarner Losh hdr->txh_id = __SHIFTIN(BWI_TX_DATA_RING, BWI_TXH_ID_RING_MASK) | 304512e36acbSWarner Losh __SHIFTIN(idx, BWI_TXH_ID_IDX_MASK); 304612e36acbSWarner Losh 304712e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_plcp, pkt_len, rate); 304812e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_fb_plcp, pkt_len, rate_fb); 304912e36acbSWarner Losh 305012e36acbSWarner Losh phy_ctrl = __SHIFTIN(mac->mac_rf.rf_ant_mode, 305112e36acbSWarner Losh BWI_TXH_PHY_C_ANTMODE_MASK); 305212e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) 305312e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_OFDM; 305412e36acbSWarner Losh else if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && rate != (2 * 1)) 305512e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_SHPREAMBLE; 305612e36acbSWarner Losh 305712e36acbSWarner Losh mac_ctrl = BWI_TXH_MAC_C_HWSEQ | BWI_TXH_MAC_C_FIRST_FRAG; 305812e36acbSWarner Losh if (!ismcast) 305912e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_ACK; 306012e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate_fb) == IEEE80211_T_OFDM) 306112e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_FB_OFDM; 306212e36acbSWarner Losh 306312e36acbSWarner Losh hdr->txh_mac_ctrl = htole32(mac_ctrl); 306412e36acbSWarner Losh hdr->txh_phy_ctrl = htole16(phy_ctrl); 306512e36acbSWarner Losh 306612e36acbSWarner Losh /* Catch any further usage */ 306712e36acbSWarner Losh hdr = NULL; 306812e36acbSWarner Losh wh = NULL; 306912e36acbSWarner Losh 307012e36acbSWarner Losh /* DMA load */ 307112e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 307212e36acbSWarner Losh bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); 307312e36acbSWarner Losh if (error && error != EFBIG) { 307412e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (1) %d\n", 307512e36acbSWarner Losh __func__, error); 307612e36acbSWarner Losh goto back; 307712e36acbSWarner Losh } 307812e36acbSWarner Losh 307912e36acbSWarner Losh if (error) { /* error == EFBIG */ 308012e36acbSWarner Losh struct mbuf *m_new; 308112e36acbSWarner Losh 308212e36acbSWarner Losh m_new = m_defrag(m, M_DONTWAIT); 308312e36acbSWarner Losh if (m_new == NULL) { 308412e36acbSWarner Losh if_printf(ifp, "%s: can't defrag TX buffer\n", 308512e36acbSWarner Losh __func__); 308612e36acbSWarner Losh error = ENOBUFS; 308712e36acbSWarner Losh goto back; 308812e36acbSWarner Losh } else { 308912e36acbSWarner Losh m = m_new; 309012e36acbSWarner Losh } 309112e36acbSWarner Losh 309212e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 309312e36acbSWarner Losh bwi_dma_buf_addr, &paddr, 309412e36acbSWarner Losh BUS_DMA_NOWAIT); 309512e36acbSWarner Losh if (error) { 309612e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (2) %d\n", 309712e36acbSWarner Losh __func__, error); 309812e36acbSWarner Losh goto back; 309912e36acbSWarner Losh } 310012e36acbSWarner Losh } 310112e36acbSWarner Losh error = 0; 310212e36acbSWarner Losh 310312e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, tb->tb_dmap, BUS_DMASYNC_PREWRITE); 310412e36acbSWarner Losh 310512e36acbSWarner Losh tb->tb_mbuf = m; 310612e36acbSWarner Losh tb->tb_ni = ni; 310712e36acbSWarner Losh 310812e36acbSWarner Losh #if 0 310912e36acbSWarner Losh p = mtod(m, const uint8_t *); 311012e36acbSWarner Losh for (i = 0; i < m->m_pkthdr.len; ++i) { 311112e36acbSWarner Losh if (i != 0 && i % 8 == 0) 311212e36acbSWarner Losh printf("\n"); 311312e36acbSWarner Losh printf("%02x ", p[i]); 311412e36acbSWarner Losh } 311512e36acbSWarner Losh printf("\n"); 311612e36acbSWarner Losh #endif 311712e36acbSWarner Losh DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", 311812e36acbSWarner Losh idx, pkt_len, m->m_pkthdr.len); 311912e36acbSWarner Losh 312012e36acbSWarner Losh /* Setup TX descriptor */ 312112e36acbSWarner Losh sc->sc_setup_txdesc(sc, rd, idx, paddr, m->m_pkthdr.len); 312212e36acbSWarner Losh bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, 312312e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 312412e36acbSWarner Losh 312512e36acbSWarner Losh /* Kick start */ 312612e36acbSWarner Losh sc->sc_start_tx(sc, rd->rdata_txrx_ctrl, idx); 312712e36acbSWarner Losh 312812e36acbSWarner Losh back: 312912e36acbSWarner Losh if (error) 313012e36acbSWarner Losh m_freem(m); 313112e36acbSWarner Losh return error; 313212e36acbSWarner Losh } 313312e36acbSWarner Losh 313412e36acbSWarner Losh static int 313512e36acbSWarner Losh bwi_encap_raw(struct bwi_softc *sc, int idx, struct mbuf *m, 313612e36acbSWarner Losh struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) 313712e36acbSWarner Losh { 313812e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 31395463c4a4SSam Leffler struct ieee80211vap *vap = ni->ni_vap; 3140515db61dSSam Leffler struct ieee80211com *ic = ni->ni_ic; 314112e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_tx_rdata[BWI_TX_DATA_RING]; 314212e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 314312e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; 314412e36acbSWarner Losh struct bwi_mac *mac; 314512e36acbSWarner Losh struct bwi_txbuf_hdr *hdr; 314612e36acbSWarner Losh struct ieee80211_frame *wh; 314712e36acbSWarner Losh uint8_t rate, rate_fb; 314812e36acbSWarner Losh uint32_t mac_ctrl; 314912e36acbSWarner Losh uint16_t phy_ctrl; 315012e36acbSWarner Losh bus_addr_t paddr; 315112e36acbSWarner Losh int ismcast, pkt_len, error; 315212e36acbSWarner Losh 315312e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 315412e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 315512e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 315612e36acbSWarner Losh 315712e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame *); 315812e36acbSWarner Losh ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 315912e36acbSWarner Losh 316012e36acbSWarner Losh /* Get 802.11 frame len before prepending TX header */ 316112e36acbSWarner Losh pkt_len = m->m_pkthdr.len + IEEE80211_CRC_LEN; 316212e36acbSWarner Losh 316312e36acbSWarner Losh /* 316412e36acbSWarner Losh * Find TX rate 316512e36acbSWarner Losh */ 316612e36acbSWarner Losh rate = params->ibp_rate0; 3167515db61dSSam Leffler if (!ieee80211_isratevalid(ic->ic_rt, rate)) { 3168515db61dSSam Leffler /* XXX fall back to mcast/mgmt rate? */ 3169c5f5009bSSam Leffler m_freem(m); 3170515db61dSSam Leffler return EINVAL; 3171515db61dSSam Leffler } 3172515db61dSSam Leffler if (params->ibp_try1 != 0) { 3173515db61dSSam Leffler rate_fb = params->ibp_rate1; 3174515db61dSSam Leffler if (!ieee80211_isratevalid(ic->ic_rt, rate_fb)) { 3175515db61dSSam Leffler /* XXX fall back to rate0? */ 3176c5f5009bSSam Leffler m_freem(m); 3177515db61dSSam Leffler return EINVAL; 3178515db61dSSam Leffler } 3179515db61dSSam Leffler } else 3180515db61dSSam Leffler rate_fb = rate; 318112e36acbSWarner Losh tb->tb_rate[0] = rate; 318212e36acbSWarner Losh tb->tb_rate[1] = rate_fb; 318312e36acbSWarner Losh sc->sc_tx_rate = rate; 318412e36acbSWarner Losh 318512e36acbSWarner Losh /* 318612e36acbSWarner Losh * TX radio tap 318712e36acbSWarner Losh */ 31885463c4a4SSam Leffler if (ieee80211_radiotap_active_vap(vap)) { 318912e36acbSWarner Losh sc->sc_tx_th.wt_flags = 0; 319012e36acbSWarner Losh /* XXX IEEE80211_BPF_CRYPTO */ 319112e36acbSWarner Losh if (wh->i_fc[1] & IEEE80211_FC1_WEP) 319212e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 319312e36acbSWarner Losh if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 319412e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 319512e36acbSWarner Losh sc->sc_tx_th.wt_rate = rate; 319612e36acbSWarner Losh 31975463c4a4SSam Leffler ieee80211_radiotap_tx(vap, m); 319812e36acbSWarner Losh } 319912e36acbSWarner Losh 320012e36acbSWarner Losh /* 320112e36acbSWarner Losh * Setup the embedded TX header 320212e36acbSWarner Losh */ 320312e36acbSWarner Losh M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); 320412e36acbSWarner Losh if (m == NULL) { 320512e36acbSWarner Losh if_printf(ifp, "%s: prepend TX header failed\n", __func__); 320612e36acbSWarner Losh return ENOBUFS; 320712e36acbSWarner Losh } 320812e36acbSWarner Losh hdr = mtod(m, struct bwi_txbuf_hdr *); 320912e36acbSWarner Losh 321012e36acbSWarner Losh bzero(hdr, sizeof(*hdr)); 321112e36acbSWarner Losh 321212e36acbSWarner Losh bcopy(wh->i_fc, hdr->txh_fc, sizeof(hdr->txh_fc)); 321312e36acbSWarner Losh bcopy(wh->i_addr1, hdr->txh_addr1, sizeof(hdr->txh_addr1)); 321412e36acbSWarner Losh 321512e36acbSWarner Losh mac_ctrl = BWI_TXH_MAC_C_HWSEQ | BWI_TXH_MAC_C_FIRST_FRAG; 321612e36acbSWarner Losh if (!ismcast && (params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { 321712e36acbSWarner Losh uint16_t dur; 321812e36acbSWarner Losh 321912e36acbSWarner Losh dur = ieee80211_ack_duration(sc->sc_rates, rate_fb, 0); 322012e36acbSWarner Losh 322112e36acbSWarner Losh hdr->txh_fb_duration = htole16(dur); 322212e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_ACK; 322312e36acbSWarner Losh } 322412e36acbSWarner Losh 322512e36acbSWarner Losh hdr->txh_id = __SHIFTIN(BWI_TX_DATA_RING, BWI_TXH_ID_RING_MASK) | 322612e36acbSWarner Losh __SHIFTIN(idx, BWI_TXH_ID_IDX_MASK); 322712e36acbSWarner Losh 322812e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_plcp, pkt_len, rate); 322912e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_fb_plcp, pkt_len, rate_fb); 323012e36acbSWarner Losh 323112e36acbSWarner Losh phy_ctrl = __SHIFTIN(mac->mac_rf.rf_ant_mode, 323212e36acbSWarner Losh BWI_TXH_PHY_C_ANTMODE_MASK); 323312e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { 323412e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_OFDM; 323512e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_FB_OFDM; 323612e36acbSWarner Losh } else if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 323712e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_SHPREAMBLE; 323812e36acbSWarner Losh 323912e36acbSWarner Losh hdr->txh_mac_ctrl = htole32(mac_ctrl); 324012e36acbSWarner Losh hdr->txh_phy_ctrl = htole16(phy_ctrl); 324112e36acbSWarner Losh 324212e36acbSWarner Losh /* Catch any further usage */ 324312e36acbSWarner Losh hdr = NULL; 324412e36acbSWarner Losh wh = NULL; 324512e36acbSWarner Losh 324612e36acbSWarner Losh /* DMA load */ 324712e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 324812e36acbSWarner Losh bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); 324912e36acbSWarner Losh if (error != 0) { 325012e36acbSWarner Losh struct mbuf *m_new; 325112e36acbSWarner Losh 325212e36acbSWarner Losh if (error != EFBIG) { 325312e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (1) %d\n", 325412e36acbSWarner Losh __func__, error); 325512e36acbSWarner Losh goto back; 325612e36acbSWarner Losh } 325712e36acbSWarner Losh m_new = m_defrag(m, M_DONTWAIT); 325812e36acbSWarner Losh if (m_new == NULL) { 325912e36acbSWarner Losh if_printf(ifp, "%s: can't defrag TX buffer\n", 326012e36acbSWarner Losh __func__); 326112e36acbSWarner Losh error = ENOBUFS; 326212e36acbSWarner Losh goto back; 326312e36acbSWarner Losh } 326412e36acbSWarner Losh m = m_new; 326512e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 326612e36acbSWarner Losh bwi_dma_buf_addr, &paddr, 326712e36acbSWarner Losh BUS_DMA_NOWAIT); 326812e36acbSWarner Losh if (error) { 326912e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (2) %d\n", 327012e36acbSWarner Losh __func__, error); 327112e36acbSWarner Losh goto back; 327212e36acbSWarner Losh } 327312e36acbSWarner Losh } 327412e36acbSWarner Losh 327512e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, tb->tb_dmap, BUS_DMASYNC_PREWRITE); 327612e36acbSWarner Losh 327712e36acbSWarner Losh tb->tb_mbuf = m; 327812e36acbSWarner Losh tb->tb_ni = ni; 327912e36acbSWarner Losh 328012e36acbSWarner Losh DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", 328112e36acbSWarner Losh idx, pkt_len, m->m_pkthdr.len); 328212e36acbSWarner Losh 328312e36acbSWarner Losh /* Setup TX descriptor */ 328412e36acbSWarner Losh sc->sc_setup_txdesc(sc, rd, idx, paddr, m->m_pkthdr.len); 328512e36acbSWarner Losh bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, 328612e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 328712e36acbSWarner Losh 328812e36acbSWarner Losh /* Kick start */ 328912e36acbSWarner Losh sc->sc_start_tx(sc, rd->rdata_txrx_ctrl, idx); 329012e36acbSWarner Losh back: 329112e36acbSWarner Losh if (error) 329212e36acbSWarner Losh m_freem(m); 329312e36acbSWarner Losh return error; 329412e36acbSWarner Losh } 329512e36acbSWarner Losh 329612e36acbSWarner Losh static void 329712e36acbSWarner Losh bwi_start_tx32(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) 329812e36acbSWarner Losh { 329912e36acbSWarner Losh idx = (idx + 1) % BWI_TX_NDESC; 330012e36acbSWarner Losh CSR_WRITE_4(sc, tx_ctrl + BWI_TX32_INDEX, 330112e36acbSWarner Losh idx * sizeof(struct bwi_desc32)); 330212e36acbSWarner Losh } 330312e36acbSWarner Losh 330412e36acbSWarner Losh static void 330512e36acbSWarner Losh bwi_start_tx64(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) 330612e36acbSWarner Losh { 330712e36acbSWarner Losh /* TODO:64 */ 330812e36acbSWarner Losh } 330912e36acbSWarner Losh 331012e36acbSWarner Losh static void 331112e36acbSWarner Losh bwi_txeof_status32(struct bwi_softc *sc) 331212e36acbSWarner Losh { 331312e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 331412e36acbSWarner Losh uint32_t val, ctrl_base; 331512e36acbSWarner Losh int end_idx; 331612e36acbSWarner Losh 331712e36acbSWarner Losh ctrl_base = sc->sc_txstats->stats_ctrl_base; 331812e36acbSWarner Losh 331912e36acbSWarner Losh val = CSR_READ_4(sc, ctrl_base + BWI_RX32_STATUS); 332012e36acbSWarner Losh end_idx = __SHIFTOUT(val, BWI_RX32_STATUS_INDEX_MASK) / 332112e36acbSWarner Losh sizeof(struct bwi_desc32); 332212e36acbSWarner Losh 332312e36acbSWarner Losh bwi_txeof_status(sc, end_idx); 332412e36acbSWarner Losh 332512e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_INDEX, 332612e36acbSWarner Losh end_idx * sizeof(struct bwi_desc32)); 332712e36acbSWarner Losh 332812e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) 332912e36acbSWarner Losh ifp->if_start(ifp); 333012e36acbSWarner Losh } 333112e36acbSWarner Losh 333212e36acbSWarner Losh static void 333312e36acbSWarner Losh bwi_txeof_status64(struct bwi_softc *sc) 333412e36acbSWarner Losh { 333512e36acbSWarner Losh /* TODO:64 */ 333612e36acbSWarner Losh } 333712e36acbSWarner Losh 333812e36acbSWarner Losh static void 333912e36acbSWarner Losh _bwi_txeof(struct bwi_softc *sc, uint16_t tx_id, int acked, int data_txcnt) 334012e36acbSWarner Losh { 334112e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 334212e36acbSWarner Losh struct bwi_txbuf_data *tbd; 334312e36acbSWarner Losh struct bwi_txbuf *tb; 334412e36acbSWarner Losh int ring_idx, buf_idx; 334512e36acbSWarner Losh struct ieee80211_node *ni; 3346b6108616SRui Paulo struct ieee80211vap *vap; 334712e36acbSWarner Losh 334812e36acbSWarner Losh if (tx_id == 0) { 334912e36acbSWarner Losh if_printf(ifp, "%s: zero tx id\n", __func__); 335012e36acbSWarner Losh return; 335112e36acbSWarner Losh } 335212e36acbSWarner Losh 335312e36acbSWarner Losh ring_idx = __SHIFTOUT(tx_id, BWI_TXH_ID_RING_MASK); 335412e36acbSWarner Losh buf_idx = __SHIFTOUT(tx_id, BWI_TXH_ID_IDX_MASK); 335512e36acbSWarner Losh 335612e36acbSWarner Losh KASSERT(ring_idx == BWI_TX_DATA_RING, ("ring_idx %d", ring_idx)); 335712e36acbSWarner Losh KASSERT(buf_idx < BWI_TX_NDESC, ("buf_idx %d", buf_idx)); 335812e36acbSWarner Losh 335912e36acbSWarner Losh tbd = &sc->sc_tx_bdata[ring_idx]; 336012e36acbSWarner Losh KASSERT(tbd->tbd_used > 0, ("tbd_used %d", tbd->tbd_used)); 336112e36acbSWarner Losh tbd->tbd_used--; 336212e36acbSWarner Losh 336312e36acbSWarner Losh tb = &tbd->tbd_buf[buf_idx]; 336412e36acbSWarner Losh DPRINTF(sc, BWI_DBG_TXEOF, "txeof idx %d, " 336512e36acbSWarner Losh "acked %d, data_txcnt %d, ni %p\n", 336612e36acbSWarner Losh buf_idx, acked, data_txcnt, tb->tb_ni); 336712e36acbSWarner Losh 336812e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); 336912e36acbSWarner Losh 337012e36acbSWarner Losh ni = tb->tb_ni; 3371b6108616SRui Paulo vap = ni->ni_vap; 337212e36acbSWarner Losh if (tb->tb_ni != NULL) { 337312e36acbSWarner Losh const struct bwi_txbuf_hdr *hdr = 337412e36acbSWarner Losh mtod(tb->tb_mbuf, const struct bwi_txbuf_hdr *); 337512e36acbSWarner Losh 337612e36acbSWarner Losh /* NB: update rate control only for unicast frames */ 337712e36acbSWarner Losh if (hdr->txh_mac_ctrl & htole32(BWI_TXH_MAC_C_ACK)) { 337812e36acbSWarner Losh /* 337912e36acbSWarner Losh * Feed back 'acked and data_txcnt'. Note that the 338012e36acbSWarner Losh * generic AMRR code only understands one tx rate 338112e36acbSWarner Losh * and the estimator doesn't handle real retry counts 338212e36acbSWarner Losh * well so to avoid over-aggressive downshifting we 338312e36acbSWarner Losh * treat any number of retries as "1". 338412e36acbSWarner Losh */ 3385b6108616SRui Paulo ieee80211_ratectl_tx_complete(vap, ni, 3386b6108616SRui Paulo (data_txcnt > 1) ? IEEE80211_RATECTL_TX_SUCCESS : 3387b6108616SRui Paulo IEEE80211_RATECTL_TX_FAILURE, &acked, NULL); 338812e36acbSWarner Losh } 338912e36acbSWarner Losh 339012e36acbSWarner Losh /* 339112e36acbSWarner Losh * Do any tx complete callback. Note this must 339212e36acbSWarner Losh * be done before releasing the node reference. 339312e36acbSWarner Losh */ 339412e36acbSWarner Losh if (tb->tb_mbuf->m_flags & M_TXCB) 339512e36acbSWarner Losh ieee80211_process_callback(ni, tb->tb_mbuf, !acked); 339612e36acbSWarner Losh 339712e36acbSWarner Losh ieee80211_free_node(tb->tb_ni); 339812e36acbSWarner Losh tb->tb_ni = NULL; 339912e36acbSWarner Losh } 340012e36acbSWarner Losh m_freem(tb->tb_mbuf); 340112e36acbSWarner Losh tb->tb_mbuf = NULL; 340212e36acbSWarner Losh 340312e36acbSWarner Losh if (tbd->tbd_used == 0) 340458fbe5abSJohn Baldwin sc->sc_tx_timer = 0; 340512e36acbSWarner Losh 340612e36acbSWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 340712e36acbSWarner Losh } 340812e36acbSWarner Losh 340912e36acbSWarner Losh static void 341012e36acbSWarner Losh bwi_txeof_status(struct bwi_softc *sc, int end_idx) 341112e36acbSWarner Losh { 341212e36acbSWarner Losh struct bwi_txstats_data *st = sc->sc_txstats; 341312e36acbSWarner Losh int idx; 341412e36acbSWarner Losh 341512e36acbSWarner Losh bus_dmamap_sync(st->stats_dtag, st->stats_dmap, BUS_DMASYNC_POSTREAD); 341612e36acbSWarner Losh 341712e36acbSWarner Losh idx = st->stats_idx; 341812e36acbSWarner Losh while (idx != end_idx) { 341912e36acbSWarner Losh const struct bwi_txstats *stats = &st->stats[idx]; 342012e36acbSWarner Losh 342112e36acbSWarner Losh if ((stats->txs_flags & BWI_TXS_F_PENDING) == 0) { 342212e36acbSWarner Losh int data_txcnt; 342312e36acbSWarner Losh 342412e36acbSWarner Losh data_txcnt = __SHIFTOUT(stats->txs_txcnt, 342512e36acbSWarner Losh BWI_TXS_TXCNT_DATA); 342612e36acbSWarner Losh _bwi_txeof(sc, le16toh(stats->txs_id), 342712e36acbSWarner Losh stats->txs_flags & BWI_TXS_F_ACKED, 342812e36acbSWarner Losh data_txcnt); 342912e36acbSWarner Losh } 343012e36acbSWarner Losh idx = (idx + 1) % BWI_TXSTATS_NDESC; 343112e36acbSWarner Losh } 343212e36acbSWarner Losh st->stats_idx = idx; 343312e36acbSWarner Losh } 343412e36acbSWarner Losh 343512e36acbSWarner Losh static void 343612e36acbSWarner Losh bwi_txeof(struct bwi_softc *sc) 343712e36acbSWarner Losh { 343812e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 343912e36acbSWarner Losh 344012e36acbSWarner Losh for (;;) { 344112e36acbSWarner Losh uint32_t tx_status0, tx_status1; 344212e36acbSWarner Losh uint16_t tx_id; 344312e36acbSWarner Losh int data_txcnt; 344412e36acbSWarner Losh 344512e36acbSWarner Losh tx_status0 = CSR_READ_4(sc, BWI_TXSTATUS0); 344612e36acbSWarner Losh if ((tx_status0 & BWI_TXSTATUS0_VALID) == 0) 344712e36acbSWarner Losh break; 344812e36acbSWarner Losh tx_status1 = CSR_READ_4(sc, BWI_TXSTATUS1); 344912e36acbSWarner Losh 345012e36acbSWarner Losh tx_id = __SHIFTOUT(tx_status0, BWI_TXSTATUS0_TXID_MASK); 345112e36acbSWarner Losh data_txcnt = __SHIFTOUT(tx_status0, 345212e36acbSWarner Losh BWI_TXSTATUS0_DATA_TXCNT_MASK); 345312e36acbSWarner Losh 345412e36acbSWarner Losh if (tx_status0 & (BWI_TXSTATUS0_AMPDU | BWI_TXSTATUS0_PENDING)) 345512e36acbSWarner Losh continue; 345612e36acbSWarner Losh 345712e36acbSWarner Losh _bwi_txeof(sc, le16toh(tx_id), tx_status0 & BWI_TXSTATUS0_ACKED, 345812e36acbSWarner Losh data_txcnt); 345912e36acbSWarner Losh } 346012e36acbSWarner Losh 346112e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) 346212e36acbSWarner Losh ifp->if_start(ifp); 346312e36acbSWarner Losh } 346412e36acbSWarner Losh 346512e36acbSWarner Losh static int 346612e36acbSWarner Losh bwi_bbp_power_on(struct bwi_softc *sc, enum bwi_clock_mode clk_mode) 346712e36acbSWarner Losh { 346812e36acbSWarner Losh bwi_power_on(sc, 1); 346912e36acbSWarner Losh return bwi_set_clock_mode(sc, clk_mode); 347012e36acbSWarner Losh } 347112e36acbSWarner Losh 347212e36acbSWarner Losh static void 347312e36acbSWarner Losh bwi_bbp_power_off(struct bwi_softc *sc) 347412e36acbSWarner Losh { 347512e36acbSWarner Losh bwi_set_clock_mode(sc, BWI_CLOCK_MODE_SLOW); 347612e36acbSWarner Losh bwi_power_off(sc, 1); 347712e36acbSWarner Losh } 347812e36acbSWarner Losh 347912e36acbSWarner Losh static int 348012e36acbSWarner Losh bwi_get_pwron_delay(struct bwi_softc *sc) 348112e36acbSWarner Losh { 348212e36acbSWarner Losh struct bwi_regwin *com, *old; 348312e36acbSWarner Losh struct bwi_clock_freq freq; 348412e36acbSWarner Losh uint32_t val; 348512e36acbSWarner Losh int error; 348612e36acbSWarner Losh 348712e36acbSWarner Losh com = &sc->sc_com_regwin; 348812e36acbSWarner Losh KASSERT(BWI_REGWIN_EXIST(com), ("no regwin")); 348912e36acbSWarner Losh 349012e36acbSWarner Losh if ((sc->sc_cap & BWI_CAP_CLKMODE) == 0) 349112e36acbSWarner Losh return 0; 349212e36acbSWarner Losh 349312e36acbSWarner Losh error = bwi_regwin_switch(sc, com, &old); 349412e36acbSWarner Losh if (error) 349512e36acbSWarner Losh return error; 349612e36acbSWarner Losh 349712e36acbSWarner Losh bwi_get_clock_freq(sc, &freq); 349812e36acbSWarner Losh 349912e36acbSWarner Losh val = CSR_READ_4(sc, BWI_PLL_ON_DELAY); 350012e36acbSWarner Losh sc->sc_pwron_delay = howmany((val + 2) * 1000000, freq.clkfreq_min); 350112e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "power on delay %u\n", sc->sc_pwron_delay); 350212e36acbSWarner Losh 350312e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 350412e36acbSWarner Losh } 350512e36acbSWarner Losh 350612e36acbSWarner Losh static int 350712e36acbSWarner Losh bwi_bus_attach(struct bwi_softc *sc) 350812e36acbSWarner Losh { 350912e36acbSWarner Losh struct bwi_regwin *bus, *old; 351012e36acbSWarner Losh int error; 351112e36acbSWarner Losh 351212e36acbSWarner Losh bus = &sc->sc_bus_regwin; 351312e36acbSWarner Losh 351412e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, &old); 351512e36acbSWarner Losh if (error) 351612e36acbSWarner Losh return error; 351712e36acbSWarner Losh 351812e36acbSWarner Losh if (!bwi_regwin_is_enabled(sc, bus)) 351912e36acbSWarner Losh bwi_regwin_enable(sc, bus, 0); 352012e36acbSWarner Losh 352112e36acbSWarner Losh /* Disable interripts */ 352212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_INTRVEC, 0); 352312e36acbSWarner Losh 352412e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 352512e36acbSWarner Losh } 352612e36acbSWarner Losh 352712e36acbSWarner Losh static const char * 352812e36acbSWarner Losh bwi_regwin_name(const struct bwi_regwin *rw) 352912e36acbSWarner Losh { 353012e36acbSWarner Losh switch (rw->rw_type) { 353112e36acbSWarner Losh case BWI_REGWIN_T_COM: 353212e36acbSWarner Losh return "COM"; 353312e36acbSWarner Losh case BWI_REGWIN_T_BUSPCI: 353412e36acbSWarner Losh return "PCI"; 353512e36acbSWarner Losh case BWI_REGWIN_T_MAC: 353612e36acbSWarner Losh return "MAC"; 353712e36acbSWarner Losh case BWI_REGWIN_T_BUSPCIE: 353812e36acbSWarner Losh return "PCIE"; 353912e36acbSWarner Losh } 354012e36acbSWarner Losh panic("unknown regwin type 0x%04x\n", rw->rw_type); 354112e36acbSWarner Losh return NULL; 354212e36acbSWarner Losh } 354312e36acbSWarner Losh 354412e36acbSWarner Losh static uint32_t 354512e36acbSWarner Losh bwi_regwin_disable_bits(struct bwi_softc *sc) 354612e36acbSWarner Losh { 354712e36acbSWarner Losh uint32_t busrev; 354812e36acbSWarner Losh 354912e36acbSWarner Losh /* XXX cache this */ 355012e36acbSWarner Losh busrev = __SHIFTOUT(CSR_READ_4(sc, BWI_ID_LO), BWI_ID_LO_BUSREV_MASK); 355112e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT | BWI_DBG_MISC, 355212e36acbSWarner Losh "bus rev %u\n", busrev); 355312e36acbSWarner Losh 355412e36acbSWarner Losh if (busrev == BWI_BUSREV_0) 355512e36acbSWarner Losh return BWI_STATE_LO_DISABLE1; 355612e36acbSWarner Losh else if (busrev == BWI_BUSREV_1) 355712e36acbSWarner Losh return BWI_STATE_LO_DISABLE2; 355812e36acbSWarner Losh else 355912e36acbSWarner Losh return (BWI_STATE_LO_DISABLE1 | BWI_STATE_LO_DISABLE2); 356012e36acbSWarner Losh } 356112e36acbSWarner Losh 356212e36acbSWarner Losh int 356312e36acbSWarner Losh bwi_regwin_is_enabled(struct bwi_softc *sc, struct bwi_regwin *rw) 356412e36acbSWarner Losh { 356512e36acbSWarner Losh uint32_t val, disable_bits; 356612e36acbSWarner Losh 356712e36acbSWarner Losh disable_bits = bwi_regwin_disable_bits(sc); 356812e36acbSWarner Losh val = CSR_READ_4(sc, BWI_STATE_LO); 356912e36acbSWarner Losh 357012e36acbSWarner Losh if ((val & (BWI_STATE_LO_CLOCK | 357112e36acbSWarner Losh BWI_STATE_LO_RESET | 357212e36acbSWarner Losh disable_bits)) == BWI_STATE_LO_CLOCK) { 357312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s is enabled\n", 357412e36acbSWarner Losh bwi_regwin_name(rw)); 357512e36acbSWarner Losh return 1; 357612e36acbSWarner Losh } else { 357712e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s is disabled\n", 357812e36acbSWarner Losh bwi_regwin_name(rw)); 357912e36acbSWarner Losh return 0; 358012e36acbSWarner Losh } 358112e36acbSWarner Losh } 358212e36acbSWarner Losh 358312e36acbSWarner Losh void 358412e36acbSWarner Losh bwi_regwin_disable(struct bwi_softc *sc, struct bwi_regwin *rw, uint32_t flags) 358512e36acbSWarner Losh { 358612e36acbSWarner Losh uint32_t state_lo, disable_bits; 358712e36acbSWarner Losh int i; 358812e36acbSWarner Losh 358912e36acbSWarner Losh state_lo = CSR_READ_4(sc, BWI_STATE_LO); 359012e36acbSWarner Losh 359112e36acbSWarner Losh /* 359212e36acbSWarner Losh * If current regwin is in 'reset' state, it was already disabled. 359312e36acbSWarner Losh */ 359412e36acbSWarner Losh if (state_lo & BWI_STATE_LO_RESET) { 359512e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, 359612e36acbSWarner Losh "%s was already disabled\n", bwi_regwin_name(rw)); 359712e36acbSWarner Losh return; 359812e36acbSWarner Losh } 359912e36acbSWarner Losh 360012e36acbSWarner Losh disable_bits = bwi_regwin_disable_bits(sc); 360112e36acbSWarner Losh 360212e36acbSWarner Losh /* 360312e36acbSWarner Losh * Disable normal clock 360412e36acbSWarner Losh */ 360512e36acbSWarner Losh state_lo = BWI_STATE_LO_CLOCK | disable_bits; 360612e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 360712e36acbSWarner Losh 360812e36acbSWarner Losh /* 360912e36acbSWarner Losh * Wait until normal clock is disabled 361012e36acbSWarner Losh */ 361112e36acbSWarner Losh #define NRETRY 1000 361212e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 361312e36acbSWarner Losh state_lo = CSR_READ_4(sc, BWI_STATE_LO); 361412e36acbSWarner Losh if (state_lo & disable_bits) 361512e36acbSWarner Losh break; 361612e36acbSWarner Losh DELAY(10); 361712e36acbSWarner Losh } 361812e36acbSWarner Losh if (i == NRETRY) { 361912e36acbSWarner Losh device_printf(sc->sc_dev, "%s disable clock timeout\n", 362012e36acbSWarner Losh bwi_regwin_name(rw)); 362112e36acbSWarner Losh } 362212e36acbSWarner Losh 362312e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 362412e36acbSWarner Losh uint32_t state_hi; 362512e36acbSWarner Losh 362612e36acbSWarner Losh state_hi = CSR_READ_4(sc, BWI_STATE_HI); 362712e36acbSWarner Losh if ((state_hi & BWI_STATE_HI_BUSY) == 0) 362812e36acbSWarner Losh break; 362912e36acbSWarner Losh DELAY(10); 363012e36acbSWarner Losh } 363112e36acbSWarner Losh if (i == NRETRY) { 363212e36acbSWarner Losh device_printf(sc->sc_dev, "%s wait BUSY unset timeout\n", 363312e36acbSWarner Losh bwi_regwin_name(rw)); 363412e36acbSWarner Losh } 363512e36acbSWarner Losh #undef NRETRY 363612e36acbSWarner Losh 363712e36acbSWarner Losh /* 363812e36acbSWarner Losh * Reset and disable regwin with gated clock 363912e36acbSWarner Losh */ 364012e36acbSWarner Losh state_lo = BWI_STATE_LO_RESET | disable_bits | 364112e36acbSWarner Losh BWI_STATE_LO_CLOCK | BWI_STATE_LO_GATED_CLOCK | 364212e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 364312e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 364412e36acbSWarner Losh 364512e36acbSWarner Losh /* Flush pending bus write */ 364612e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 364712e36acbSWarner Losh DELAY(1); 364812e36acbSWarner Losh 364912e36acbSWarner Losh /* Reset and disable regwin */ 365012e36acbSWarner Losh state_lo = BWI_STATE_LO_RESET | disable_bits | 365112e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 365212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 365312e36acbSWarner Losh 365412e36acbSWarner Losh /* Flush pending bus write */ 365512e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 365612e36acbSWarner Losh DELAY(1); 365712e36acbSWarner Losh } 365812e36acbSWarner Losh 365912e36acbSWarner Losh void 366012e36acbSWarner Losh bwi_regwin_enable(struct bwi_softc *sc, struct bwi_regwin *rw, uint32_t flags) 366112e36acbSWarner Losh { 366212e36acbSWarner Losh uint32_t state_lo, state_hi, imstate; 366312e36acbSWarner Losh 366412e36acbSWarner Losh bwi_regwin_disable(sc, rw, flags); 366512e36acbSWarner Losh 366612e36acbSWarner Losh /* Reset regwin with gated clock */ 366712e36acbSWarner Losh state_lo = BWI_STATE_LO_RESET | 366812e36acbSWarner Losh BWI_STATE_LO_CLOCK | 366912e36acbSWarner Losh BWI_STATE_LO_GATED_CLOCK | 367012e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 367112e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 367212e36acbSWarner Losh 367312e36acbSWarner Losh /* Flush pending bus write */ 367412e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 367512e36acbSWarner Losh DELAY(1); 367612e36acbSWarner Losh 367712e36acbSWarner Losh state_hi = CSR_READ_4(sc, BWI_STATE_HI); 367812e36acbSWarner Losh if (state_hi & BWI_STATE_HI_SERROR) 367912e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_HI, 0); 368012e36acbSWarner Losh 368112e36acbSWarner Losh imstate = CSR_READ_4(sc, BWI_IMSTATE); 368212e36acbSWarner Losh if (imstate & (BWI_IMSTATE_INBAND_ERR | BWI_IMSTATE_TIMEOUT)) { 368312e36acbSWarner Losh imstate &= ~(BWI_IMSTATE_INBAND_ERR | BWI_IMSTATE_TIMEOUT); 368412e36acbSWarner Losh CSR_WRITE_4(sc, BWI_IMSTATE, imstate); 368512e36acbSWarner Losh } 368612e36acbSWarner Losh 368712e36acbSWarner Losh /* Enable regwin with gated clock */ 368812e36acbSWarner Losh state_lo = BWI_STATE_LO_CLOCK | 368912e36acbSWarner Losh BWI_STATE_LO_GATED_CLOCK | 369012e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 369112e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 369212e36acbSWarner Losh 369312e36acbSWarner Losh /* Flush pending bus write */ 369412e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 369512e36acbSWarner Losh DELAY(1); 369612e36acbSWarner Losh 369712e36acbSWarner Losh /* Enable regwin with normal clock */ 369812e36acbSWarner Losh state_lo = BWI_STATE_LO_CLOCK | 369912e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 370012e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 370112e36acbSWarner Losh 370212e36acbSWarner Losh /* Flush pending bus write */ 370312e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 370412e36acbSWarner Losh DELAY(1); 370512e36acbSWarner Losh } 370612e36acbSWarner Losh 370712e36acbSWarner Losh static void 370812e36acbSWarner Losh bwi_set_bssid(struct bwi_softc *sc, const uint8_t *bssid) 370912e36acbSWarner Losh { 371012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 371112e36acbSWarner Losh struct bwi_mac *mac; 371212e36acbSWarner Losh struct bwi_myaddr_bssid buf; 371312e36acbSWarner Losh const uint8_t *p; 371412e36acbSWarner Losh uint32_t val; 371512e36acbSWarner Losh int n, i; 371612e36acbSWarner Losh 371712e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 371812e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 371912e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 372012e36acbSWarner Losh 372112e36acbSWarner Losh bwi_set_addr_filter(sc, BWI_ADDR_FILTER_BSSID, bssid); 372212e36acbSWarner Losh 372312e36acbSWarner Losh bcopy(IF_LLADDR(ifp), buf.myaddr, sizeof(buf.myaddr)); 372412e36acbSWarner Losh bcopy(bssid, buf.bssid, sizeof(buf.bssid)); 372512e36acbSWarner Losh 372612e36acbSWarner Losh n = sizeof(buf) / sizeof(val); 372712e36acbSWarner Losh p = (const uint8_t *)&buf; 372812e36acbSWarner Losh for (i = 0; i < n; ++i) { 372912e36acbSWarner Losh int j; 373012e36acbSWarner Losh 373112e36acbSWarner Losh val = 0; 373212e36acbSWarner Losh for (j = 0; j < sizeof(val); ++j) 373312e36acbSWarner Losh val |= ((uint32_t)(*p++)) << (j * 8); 373412e36acbSWarner Losh 373512e36acbSWarner Losh TMPLT_WRITE_4(mac, 0x20 + (i * sizeof(val)), val); 373612e36acbSWarner Losh } 373712e36acbSWarner Losh } 373812e36acbSWarner Losh 373912e36acbSWarner Losh static void 374012e36acbSWarner Losh bwi_updateslot(struct ifnet *ifp) 374112e36acbSWarner Losh { 374212e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 374312e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 374412e36acbSWarner Losh struct bwi_mac *mac; 374512e36acbSWarner Losh 374612e36acbSWarner Losh BWI_LOCK(sc); 374712e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 374812e36acbSWarner Losh DPRINTF(sc, BWI_DBG_80211, "%s\n", __func__); 374912e36acbSWarner Losh 375012e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 375112e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 375212e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 375312e36acbSWarner Losh 375412e36acbSWarner Losh bwi_mac_updateslot(mac, (ic->ic_flags & IEEE80211_F_SHSLOT)); 375512e36acbSWarner Losh } 375612e36acbSWarner Losh BWI_UNLOCK(sc); 375712e36acbSWarner Losh } 375812e36acbSWarner Losh 375912e36acbSWarner Losh static void 376012e36acbSWarner Losh bwi_calibrate(void *xsc) 376112e36acbSWarner Losh { 376212e36acbSWarner Losh struct bwi_softc *sc = xsc; 376312e36acbSWarner Losh #ifdef INVARIANTS 376412e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 376512e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 376612e36acbSWarner Losh #endif 376712e36acbSWarner Losh struct bwi_mac *mac; 376812e36acbSWarner Losh 376912e36acbSWarner Losh BWI_ASSERT_LOCKED(sc); 377012e36acbSWarner Losh 377112e36acbSWarner Losh KASSERT(ic->ic_opmode != IEEE80211_M_MONITOR, 377212e36acbSWarner Losh ("opmode %d", ic->ic_opmode)); 377312e36acbSWarner Losh 377412e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 377512e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 377612e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 377712e36acbSWarner Losh 377812e36acbSWarner Losh bwi_mac_calibrate_txpower(mac, sc->sc_txpwrcb_type); 377912e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; 378012e36acbSWarner Losh 378112e36acbSWarner Losh /* XXX 15 seconds */ 378212e36acbSWarner Losh callout_reset(&sc->sc_calib_ch, hz * 15, bwi_calibrate, sc); 378312e36acbSWarner Losh } 378412e36acbSWarner Losh 378512e36acbSWarner Losh static int 378612e36acbSWarner Losh bwi_calc_rssi(struct bwi_softc *sc, const struct bwi_rxbuf_hdr *hdr) 378712e36acbSWarner Losh { 378812e36acbSWarner Losh struct bwi_mac *mac; 378912e36acbSWarner Losh 379012e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 379112e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 379212e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 379312e36acbSWarner Losh 379412e36acbSWarner Losh return bwi_rf_calc_rssi(mac, hdr); 379512e36acbSWarner Losh } 379612e36acbSWarner Losh 379712e36acbSWarner Losh static int 379812e36acbSWarner Losh bwi_calc_noise(struct bwi_softc *sc) 379912e36acbSWarner Losh { 380012e36acbSWarner Losh struct bwi_mac *mac; 380112e36acbSWarner Losh 380212e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 380312e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 380412e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 380512e36acbSWarner Losh 380612e36acbSWarner Losh return bwi_rf_calc_noise(mac); 380712e36acbSWarner Losh } 380812e36acbSWarner Losh 380912e36acbSWarner Losh static __inline uint8_t 381012e36acbSWarner Losh bwi_ofdm_plcp2rate(const uint32_t *plcp0) 381112e36acbSWarner Losh { 381212e36acbSWarner Losh uint32_t plcp; 381312e36acbSWarner Losh uint8_t plcp_rate; 381412e36acbSWarner Losh 381512e36acbSWarner Losh plcp = le32toh(*plcp0); 381612e36acbSWarner Losh plcp_rate = __SHIFTOUT(plcp, IEEE80211_OFDM_PLCP_RATE_MASK); 381712e36acbSWarner Losh return ieee80211_plcp2rate(plcp_rate, IEEE80211_T_OFDM); 381812e36acbSWarner Losh } 381912e36acbSWarner Losh 382012e36acbSWarner Losh static __inline uint8_t 382112e36acbSWarner Losh bwi_ds_plcp2rate(const struct ieee80211_ds_plcp_hdr *hdr) 382212e36acbSWarner Losh { 382312e36acbSWarner Losh return ieee80211_plcp2rate(hdr->i_signal, IEEE80211_T_DS); 382412e36acbSWarner Losh } 382512e36acbSWarner Losh 382612e36acbSWarner Losh static void 38275463c4a4SSam Leffler bwi_rx_radiotap(struct bwi_softc *sc, struct mbuf *m, 382812e36acbSWarner Losh struct bwi_rxbuf_hdr *hdr, const void *plcp, int rate, int rssi, int noise) 382912e36acbSWarner Losh { 383012e36acbSWarner Losh const struct ieee80211_frame_min *wh; 383112e36acbSWarner Losh 383212e36acbSWarner Losh sc->sc_rx_th.wr_flags = IEEE80211_RADIOTAP_F_FCS; 383312e36acbSWarner Losh if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_SHPREAMBLE) 383412e36acbSWarner Losh sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 383512e36acbSWarner Losh 383612e36acbSWarner Losh wh = mtod(m, const struct ieee80211_frame_min *); 383712e36acbSWarner Losh if (wh->i_fc[1] & IEEE80211_FC1_WEP) 383812e36acbSWarner Losh sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP; 383912e36acbSWarner Losh 384012e36acbSWarner Losh sc->sc_rx_th.wr_tsf = hdr->rxh_tsf; /* No endian convertion */ 384112e36acbSWarner Losh sc->sc_rx_th.wr_rate = rate; 384212e36acbSWarner Losh sc->sc_rx_th.wr_antsignal = rssi; 384312e36acbSWarner Losh sc->sc_rx_th.wr_antnoise = noise; 384412e36acbSWarner Losh } 384512e36acbSWarner Losh 384612e36acbSWarner Losh static void 384712e36acbSWarner Losh bwi_led_attach(struct bwi_softc *sc) 384812e36acbSWarner Losh { 384912e36acbSWarner Losh const uint8_t *led_act = NULL; 385012e36acbSWarner Losh uint16_t gpio, val[BWI_LED_MAX]; 385112e36acbSWarner Losh int i; 385212e36acbSWarner Losh 385312e36acbSWarner Losh #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 385412e36acbSWarner Losh 385512e36acbSWarner Losh for (i = 0; i < N(bwi_vendor_led_act); ++i) { 385612e36acbSWarner Losh if (sc->sc_pci_subvid == bwi_vendor_led_act[i].vid) { 385712e36acbSWarner Losh led_act = bwi_vendor_led_act[i].led_act; 385812e36acbSWarner Losh break; 385912e36acbSWarner Losh } 386012e36acbSWarner Losh } 386112e36acbSWarner Losh if (led_act == NULL) 386212e36acbSWarner Losh led_act = bwi_default_led_act; 386312e36acbSWarner Losh 386412e36acbSWarner Losh #undef N 386512e36acbSWarner Losh 386612e36acbSWarner Losh gpio = bwi_read_sprom(sc, BWI_SPROM_GPIO01); 386712e36acbSWarner Losh val[0] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_0); 386812e36acbSWarner Losh val[1] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_1); 386912e36acbSWarner Losh 387012e36acbSWarner Losh gpio = bwi_read_sprom(sc, BWI_SPROM_GPIO23); 387112e36acbSWarner Losh val[2] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_2); 387212e36acbSWarner Losh val[3] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_3); 387312e36acbSWarner Losh 387412e36acbSWarner Losh for (i = 0; i < BWI_LED_MAX; ++i) { 387512e36acbSWarner Losh struct bwi_led *led = &sc->sc_leds[i]; 387612e36acbSWarner Losh 387712e36acbSWarner Losh if (val[i] == 0xff) { 387812e36acbSWarner Losh led->l_act = led_act[i]; 387912e36acbSWarner Losh } else { 388012e36acbSWarner Losh if (val[i] & BWI_LED_ACT_LOW) 388112e36acbSWarner Losh led->l_flags |= BWI_LED_F_ACTLOW; 388212e36acbSWarner Losh led->l_act = __SHIFTOUT(val[i], BWI_LED_ACT_MASK); 388312e36acbSWarner Losh } 388412e36acbSWarner Losh led->l_mask = (1 << i); 388512e36acbSWarner Losh 388612e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_BLINK_SLOW || 388712e36acbSWarner Losh led->l_act == BWI_LED_ACT_BLINK_POLL || 388812e36acbSWarner Losh led->l_act == BWI_LED_ACT_BLINK) { 388912e36acbSWarner Losh led->l_flags |= BWI_LED_F_BLINK; 389012e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_BLINK_POLL) 389112e36acbSWarner Losh led->l_flags |= BWI_LED_F_POLLABLE; 389212e36acbSWarner Losh else if (led->l_act == BWI_LED_ACT_BLINK_SLOW) 389312e36acbSWarner Losh led->l_flags |= BWI_LED_F_SLOW; 389412e36acbSWarner Losh 389512e36acbSWarner Losh if (sc->sc_blink_led == NULL) { 389612e36acbSWarner Losh sc->sc_blink_led = led; 389712e36acbSWarner Losh if (led->l_flags & BWI_LED_F_SLOW) 389812e36acbSWarner Losh BWI_LED_SLOWDOWN(sc->sc_led_idle); 389912e36acbSWarner Losh } 390012e36acbSWarner Losh } 390112e36acbSWarner Losh 390212e36acbSWarner Losh DPRINTF(sc, BWI_DBG_LED | BWI_DBG_ATTACH, 390312e36acbSWarner Losh "%dth led, act %d, lowact %d\n", i, 390412e36acbSWarner Losh led->l_act, led->l_flags & BWI_LED_F_ACTLOW); 390512e36acbSWarner Losh } 390658fbe5abSJohn Baldwin callout_init_mtx(&sc->sc_led_blink_ch, &sc->sc_mtx, 0); 390712e36acbSWarner Losh } 390812e36acbSWarner Losh 390912e36acbSWarner Losh static __inline uint16_t 391012e36acbSWarner Losh bwi_led_onoff(const struct bwi_led *led, uint16_t val, int on) 391112e36acbSWarner Losh { 391212e36acbSWarner Losh if (led->l_flags & BWI_LED_F_ACTLOW) 391312e36acbSWarner Losh on = !on; 391412e36acbSWarner Losh if (on) 391512e36acbSWarner Losh val |= led->l_mask; 391612e36acbSWarner Losh else 391712e36acbSWarner Losh val &= ~led->l_mask; 391812e36acbSWarner Losh return val; 391912e36acbSWarner Losh } 392012e36acbSWarner Losh 392112e36acbSWarner Losh static void 392212e36acbSWarner Losh bwi_led_newstate(struct bwi_softc *sc, enum ieee80211_state nstate) 392312e36acbSWarner Losh { 392412e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 392512e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 392612e36acbSWarner Losh uint16_t val; 392712e36acbSWarner Losh int i; 392812e36acbSWarner Losh 392912e36acbSWarner Losh if (nstate == IEEE80211_S_INIT) { 393012e36acbSWarner Losh callout_stop(&sc->sc_led_blink_ch); 393112e36acbSWarner Losh sc->sc_led_blinking = 0; 393212e36acbSWarner Losh } 393312e36acbSWarner Losh 393412e36acbSWarner Losh if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 393512e36acbSWarner Losh return; 393612e36acbSWarner Losh 393712e36acbSWarner Losh val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); 393812e36acbSWarner Losh for (i = 0; i < BWI_LED_MAX; ++i) { 393912e36acbSWarner Losh struct bwi_led *led = &sc->sc_leds[i]; 394012e36acbSWarner Losh int on; 394112e36acbSWarner Losh 394212e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_UNKN || 394312e36acbSWarner Losh led->l_act == BWI_LED_ACT_NULL) 394412e36acbSWarner Losh continue; 394512e36acbSWarner Losh 394612e36acbSWarner Losh if ((led->l_flags & BWI_LED_F_BLINK) && 394712e36acbSWarner Losh nstate != IEEE80211_S_INIT) 394812e36acbSWarner Losh continue; 394912e36acbSWarner Losh 395012e36acbSWarner Losh switch (led->l_act) { 395112e36acbSWarner Losh case BWI_LED_ACT_ON: /* Always on */ 395212e36acbSWarner Losh on = 1; 395312e36acbSWarner Losh break; 395412e36acbSWarner Losh case BWI_LED_ACT_OFF: /* Always off */ 395512e36acbSWarner Losh case BWI_LED_ACT_5GHZ: /* TODO: 11A */ 395612e36acbSWarner Losh on = 0; 395712e36acbSWarner Losh break; 395812e36acbSWarner Losh default: 395912e36acbSWarner Losh on = 1; 396012e36acbSWarner Losh switch (nstate) { 396112e36acbSWarner Losh case IEEE80211_S_INIT: 396212e36acbSWarner Losh on = 0; 396312e36acbSWarner Losh break; 396412e36acbSWarner Losh case IEEE80211_S_RUN: 396512e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_11G && 396612e36acbSWarner Losh ic->ic_curmode != IEEE80211_MODE_11G) 396712e36acbSWarner Losh on = 0; 396812e36acbSWarner Losh break; 396912e36acbSWarner Losh default: 397012e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_ASSOC) 397112e36acbSWarner Losh on = 0; 397212e36acbSWarner Losh break; 397312e36acbSWarner Losh } 397412e36acbSWarner Losh break; 397512e36acbSWarner Losh } 397612e36acbSWarner Losh 397712e36acbSWarner Losh val = bwi_led_onoff(led, val, on); 397812e36acbSWarner Losh } 397912e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); 398012e36acbSWarner Losh } 398112e36acbSWarner Losh static void 398212e36acbSWarner Losh bwi_led_event(struct bwi_softc *sc, int event) 398312e36acbSWarner Losh { 398412e36acbSWarner Losh struct bwi_led *led = sc->sc_blink_led; 398512e36acbSWarner Losh int rate; 398612e36acbSWarner Losh 398712e36acbSWarner Losh if (event == BWI_LED_EVENT_POLL) { 398812e36acbSWarner Losh if ((led->l_flags & BWI_LED_F_POLLABLE) == 0) 398912e36acbSWarner Losh return; 399012e36acbSWarner Losh if (ticks - sc->sc_led_ticks < sc->sc_led_idle) 399112e36acbSWarner Losh return; 399212e36acbSWarner Losh } 399312e36acbSWarner Losh 399412e36acbSWarner Losh sc->sc_led_ticks = ticks; 399512e36acbSWarner Losh if (sc->sc_led_blinking) 399612e36acbSWarner Losh return; 399712e36acbSWarner Losh 399812e36acbSWarner Losh switch (event) { 399912e36acbSWarner Losh case BWI_LED_EVENT_RX: 400012e36acbSWarner Losh rate = sc->sc_rx_rate; 400112e36acbSWarner Losh break; 400212e36acbSWarner Losh case BWI_LED_EVENT_TX: 400312e36acbSWarner Losh rate = sc->sc_tx_rate; 400412e36acbSWarner Losh break; 400512e36acbSWarner Losh case BWI_LED_EVENT_POLL: 400612e36acbSWarner Losh rate = 0; 400712e36acbSWarner Losh break; 400812e36acbSWarner Losh default: 400912e36acbSWarner Losh panic("unknown LED event %d\n", event); 401012e36acbSWarner Losh break; 401112e36acbSWarner Losh } 401212e36acbSWarner Losh bwi_led_blink_start(sc, bwi_led_duration[rate].on_dur, 401312e36acbSWarner Losh bwi_led_duration[rate].off_dur); 401412e36acbSWarner Losh } 401512e36acbSWarner Losh 401612e36acbSWarner Losh static void 401712e36acbSWarner Losh bwi_led_blink_start(struct bwi_softc *sc, int on_dur, int off_dur) 401812e36acbSWarner Losh { 401912e36acbSWarner Losh struct bwi_led *led = sc->sc_blink_led; 402012e36acbSWarner Losh uint16_t val; 402112e36acbSWarner Losh 402212e36acbSWarner Losh val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); 402312e36acbSWarner Losh val = bwi_led_onoff(led, val, 1); 402412e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); 402512e36acbSWarner Losh 402612e36acbSWarner Losh if (led->l_flags & BWI_LED_F_SLOW) { 402712e36acbSWarner Losh BWI_LED_SLOWDOWN(on_dur); 402812e36acbSWarner Losh BWI_LED_SLOWDOWN(off_dur); 402912e36acbSWarner Losh } 403012e36acbSWarner Losh 403112e36acbSWarner Losh sc->sc_led_blinking = 1; 403212e36acbSWarner Losh sc->sc_led_blink_offdur = off_dur; 403312e36acbSWarner Losh 403412e36acbSWarner Losh callout_reset(&sc->sc_led_blink_ch, on_dur, bwi_led_blink_next, sc); 403512e36acbSWarner Losh } 403612e36acbSWarner Losh 403712e36acbSWarner Losh static void 403812e36acbSWarner Losh bwi_led_blink_next(void *xsc) 403912e36acbSWarner Losh { 404012e36acbSWarner Losh struct bwi_softc *sc = xsc; 404112e36acbSWarner Losh uint16_t val; 404212e36acbSWarner Losh 404312e36acbSWarner Losh val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); 404412e36acbSWarner Losh val = bwi_led_onoff(sc->sc_blink_led, val, 0); 404512e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); 404612e36acbSWarner Losh 404712e36acbSWarner Losh callout_reset(&sc->sc_led_blink_ch, sc->sc_led_blink_offdur, 404812e36acbSWarner Losh bwi_led_blink_end, sc); 404912e36acbSWarner Losh } 405012e36acbSWarner Losh 405112e36acbSWarner Losh static void 405212e36acbSWarner Losh bwi_led_blink_end(void *xsc) 405312e36acbSWarner Losh { 405412e36acbSWarner Losh struct bwi_softc *sc = xsc; 405512e36acbSWarner Losh sc->sc_led_blinking = 0; 405612e36acbSWarner Losh } 405712e36acbSWarner Losh 405812e36acbSWarner Losh static void 405912e36acbSWarner Losh bwi_restart(void *xsc, int pending) 406012e36acbSWarner Losh { 406112e36acbSWarner Losh struct bwi_softc *sc = xsc; 406212e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 406312e36acbSWarner Losh 406412e36acbSWarner Losh if_printf(ifp, "%s begin, help!\n", __func__); 406512e36acbSWarner Losh BWI_LOCK(sc); 406612e36acbSWarner Losh bwi_init_statechg(xsc, 0); 406712e36acbSWarner Losh #if 0 406812e36acbSWarner Losh bwi_start_locked(ifp); 406912e36acbSWarner Losh #endif 407007db17ceSWarner Losh BWI_UNLOCK(sc); 407112e36acbSWarner Losh } 4072