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