mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
7a79cebfba
connectivity interact with the net80211 stack. Historical background: originally wireless devices created an interface, just like Ethernet devices do. Name of an interface matched the name of the driver that created. Later, wlan(4) layer was introduced, and the wlanX interfaces become the actual interface, leaving original ones as "a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer and a driver became a mix of methods that pass a pointer to struct ifnet as identifier and methods that pass pointer to struct ieee80211com. From user point of view, the parent interface just hangs on in the ifconfig list, and user can't do anything useful with it. Now, the struct ifnet goes away. The struct ieee80211com is the only KPI between a device driver and net80211. Details: - The struct ieee80211com is embedded into drivers softc. - Packets are sent via new ic_transmit method, which is very much like the previous if_transmit. - Bringing parent up/down is done via new ic_parent method, which notifies driver about any changes: number of wlan(4) interfaces, number of them in promisc or allmulti state. - Device specific ioctls (if any) are received on new ic_ioctl method. - Packets/errors accounting are done by the stack. In certain cases, when driver experiences errors and can not attribute them to any specific interface, driver updates ic_oerrors or ic_ierrors counters. Details on interface configuration with new world order: - A sequence of commands needed to bring up wireless DOESN"T change. - /etc/rc.conf parameters DON'T change. - List of devices that can be used to create wlan(4) interfaces is now provided by net.wlan.devices sysctl. Most drivers in this change were converted by me, except of wpi(4), that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann, Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in testing. Reviewed by: adrian Sponsored by: Netflix Sponsored by: Nginx, Inc.
1974 lines
46 KiB
C
1974 lines
46 KiB
C
/*
|
|
* Copyright (c) 2007 The DragonFly Project. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The DragonFly Project
|
|
* by Sepherosa Ziehau <sepherosa@gmail.com>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name of The DragonFly Project nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific, prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $DragonFly: src/sys/dev/netif/bwi/bwimac.c,v 1.13 2008/02/15 11:15:38 sephe Exp $
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_bwi.h"
|
|
#include "opt_wlan.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/linker.h>
|
|
#include <sys/firmware.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_var.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/ethernet.h>
|
|
#include <net/if_llc.h>
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
#include <net80211/ieee80211_radiotap.h>
|
|
#include <net80211/ieee80211_amrr.h>
|
|
#include <net80211/ieee80211_phy.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/bwi/bitops.h>
|
|
#include <dev/bwi/if_bwireg.h>
|
|
#include <dev/bwi/if_bwivar.h>
|
|
#include <dev/bwi/bwimac.h>
|
|
#include <dev/bwi/bwirf.h>
|
|
#include <dev/bwi/bwiphy.h>
|
|
|
|
struct bwi_retry_lim {
|
|
uint16_t shretry;
|
|
uint16_t shretry_fb;
|
|
uint16_t lgretry;
|
|
uint16_t lgretry_fb;
|
|
};
|
|
|
|
static int bwi_mac_test(struct bwi_mac *);
|
|
static int bwi_mac_get_property(struct bwi_mac *);
|
|
|
|
static void bwi_mac_set_retry_lim(struct bwi_mac *,
|
|
const struct bwi_retry_lim *);
|
|
static void bwi_mac_set_ackrates(struct bwi_mac *,
|
|
const struct ieee80211_rate_table *rt,
|
|
const struct ieee80211_rateset *);
|
|
|
|
static int bwi_mac_gpio_init(struct bwi_mac *);
|
|
static int bwi_mac_gpio_fini(struct bwi_mac *);
|
|
static void bwi_mac_opmode_init(struct bwi_mac *);
|
|
static void bwi_mac_hostflags_init(struct bwi_mac *);
|
|
static void bwi_mac_bss_param_init(struct bwi_mac *);
|
|
|
|
static void bwi_mac_fw_free(struct bwi_mac *);
|
|
static int bwi_mac_fw_load(struct bwi_mac *);
|
|
static int bwi_mac_fw_init(struct bwi_mac *);
|
|
static int bwi_mac_fw_load_iv(struct bwi_mac *, const struct firmware *);
|
|
|
|
static void bwi_mac_setup_tpctl(struct bwi_mac *);
|
|
static void bwi_mac_adjust_tpctl(struct bwi_mac *, int, int);
|
|
|
|
static void bwi_mac_lock(struct bwi_mac *);
|
|
static void bwi_mac_unlock(struct bwi_mac *);
|
|
|
|
static const uint8_t bwi_sup_macrev[] = { 2, 4, 5, 6, 7, 9, 10 };
|
|
|
|
void
|
|
bwi_tmplt_write_4(struct bwi_mac *mac, uint32_t ofs, uint32_t val)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
if (mac->mac_flags & BWI_MAC_F_BSWAP)
|
|
val = bswap32(val);
|
|
|
|
CSR_WRITE_4(sc, BWI_MAC_TMPLT_CTRL, ofs);
|
|
CSR_WRITE_4(sc, BWI_MAC_TMPLT_DATA, val);
|
|
}
|
|
|
|
void
|
|
bwi_hostflags_write(struct bwi_mac *mac, uint64_t flags)
|
|
{
|
|
uint64_t val;
|
|
|
|
val = flags & 0xffff;
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_LO, val);
|
|
|
|
val = (flags >> 16) & 0xffff;
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_MI, val);
|
|
|
|
/* HI has unclear meaning, so leave it as it is */
|
|
}
|
|
|
|
uint64_t
|
|
bwi_hostflags_read(struct bwi_mac *mac)
|
|
{
|
|
uint64_t flags, val;
|
|
|
|
/* HI has unclear meaning, so don't touch it */
|
|
flags = 0;
|
|
|
|
val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_MI);
|
|
flags |= val << 16;
|
|
|
|
val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_LO);
|
|
flags |= val;
|
|
|
|
return flags;
|
|
}
|
|
|
|
uint16_t
|
|
bwi_memobj_read_2(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
uint32_t data_reg;
|
|
int ofs;
|
|
|
|
data_reg = BWI_MOBJ_DATA;
|
|
ofs = ofs0 / 4;
|
|
|
|
if (ofs0 % 4 != 0)
|
|
data_reg = BWI_MOBJ_DATA_UNALIGN;
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
|
|
return CSR_READ_2(sc, data_reg);
|
|
}
|
|
|
|
uint32_t
|
|
bwi_memobj_read_4(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
int ofs;
|
|
|
|
ofs = ofs0 / 4;
|
|
if (ofs0 % 4 != 0) {
|
|
uint32_t ret;
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
|
|
ret = CSR_READ_2(sc, BWI_MOBJ_DATA_UNALIGN);
|
|
ret <<= 16;
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
|
|
BWI_MOBJ_CTRL_VAL(obj_id, ofs + 1));
|
|
ret |= CSR_READ_2(sc, BWI_MOBJ_DATA);
|
|
|
|
return ret;
|
|
} else {
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
|
|
return CSR_READ_4(sc, BWI_MOBJ_DATA);
|
|
}
|
|
}
|
|
|
|
void
|
|
bwi_memobj_write_2(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0,
|
|
uint16_t v)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
uint32_t data_reg;
|
|
int ofs;
|
|
|
|
data_reg = BWI_MOBJ_DATA;
|
|
ofs = ofs0 / 4;
|
|
|
|
if (ofs0 % 4 != 0)
|
|
data_reg = BWI_MOBJ_DATA_UNALIGN;
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
|
|
CSR_WRITE_2(sc, data_reg, v);
|
|
}
|
|
|
|
void
|
|
bwi_memobj_write_4(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0,
|
|
uint32_t v)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
int ofs;
|
|
|
|
ofs = ofs0 / 4;
|
|
if (ofs0 % 4 != 0) {
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
|
|
CSR_WRITE_2(sc, BWI_MOBJ_DATA_UNALIGN, v >> 16);
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
|
|
BWI_MOBJ_CTRL_VAL(obj_id, ofs + 1));
|
|
CSR_WRITE_2(sc, BWI_MOBJ_DATA, v & 0xffff);
|
|
} else {
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
|
|
CSR_WRITE_4(sc, BWI_MOBJ_DATA, v);
|
|
}
|
|
}
|
|
|
|
int
|
|
bwi_mac_lateattach(struct bwi_mac *mac)
|
|
{
|
|
int error;
|
|
|
|
if (mac->mac_rev >= 5)
|
|
CSR_READ_4(mac->mac_sc, BWI_STATE_HI); /* dummy read */
|
|
|
|
bwi_mac_reset(mac, 1);
|
|
|
|
error = bwi_phy_attach(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
error = bwi_rf_attach(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
/* Link 11B/G PHY, unlink 11A PHY */
|
|
if (mac->mac_phy.phy_mode == IEEE80211_MODE_11A)
|
|
bwi_mac_reset(mac, 0);
|
|
else
|
|
bwi_mac_reset(mac, 1);
|
|
|
|
error = bwi_mac_test(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
error = bwi_mac_get_property(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
error = bwi_rf_map_txpower(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
bwi_rf_off(mac);
|
|
CSR_WRITE_2(mac->mac_sc, BWI_BBP_ATTEN, BWI_BBP_ATTEN_MAGIC);
|
|
bwi_regwin_disable(mac->mac_sc, &mac->mac_regwin, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
bwi_mac_init(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
int error, i;
|
|
|
|
/* Clear MAC/PHY/RF states */
|
|
bwi_mac_setup_tpctl(mac);
|
|
bwi_rf_clear_state(&mac->mac_rf);
|
|
bwi_phy_clear_state(&mac->mac_phy);
|
|
|
|
/* Enable MAC and linked it to PHY */
|
|
if (!bwi_regwin_is_enabled(sc, &mac->mac_regwin))
|
|
bwi_mac_reset(mac, 1);
|
|
|
|
/* Initialize backplane */
|
|
error = bwi_bus_init(sc, mac);
|
|
if (error)
|
|
return error;
|
|
|
|
/* do timeout fixup */
|
|
if (sc->sc_bus_regwin.rw_rev <= 5 &&
|
|
sc->sc_bus_regwin.rw_type != BWI_REGWIN_T_BUSPCIE) {
|
|
CSR_SETBITS_4(sc, BWI_CONF_LO,
|
|
__SHIFTIN(BWI_CONF_LO_SERVTO, BWI_CONF_LO_SERVTO_MASK) |
|
|
__SHIFTIN(BWI_CONF_LO_REQTO, BWI_CONF_LO_REQTO_MASK));
|
|
}
|
|
|
|
/* Calibrate PHY */
|
|
error = bwi_phy_calibrate(mac);
|
|
if (error) {
|
|
device_printf(sc->sc_dev, "PHY calibrate failed\n");
|
|
return error;
|
|
}
|
|
|
|
/* Prepare to initialize firmware */
|
|
CSR_WRITE_4(sc, BWI_MAC_STATUS,
|
|
BWI_MAC_STATUS_UCODE_JUMP0 |
|
|
BWI_MAC_STATUS_IHREN);
|
|
|
|
/*
|
|
* Load and initialize firmwares
|
|
*/
|
|
error = bwi_mac_fw_load(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
error = bwi_mac_gpio_init(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
error = bwi_mac_fw_init(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
/*
|
|
* Turn on RF
|
|
*/
|
|
bwi_rf_on(mac);
|
|
|
|
/* TODO: LED, hardware rf enabled is only related to LED setting */
|
|
|
|
/*
|
|
* Initialize PHY
|
|
*/
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0);
|
|
bwi_phy_init(mac);
|
|
|
|
/* TODO: interference mitigation */
|
|
|
|
/*
|
|
* Setup antenna mode
|
|
*/
|
|
bwi_rf_set_ant_mode(mac, mac->mac_rf.rf_ant_mode);
|
|
|
|
/*
|
|
* Initialize operation mode (RX configuration)
|
|
*/
|
|
bwi_mac_opmode_init(mac);
|
|
|
|
/* set up Beacon interval */
|
|
if (mac->mac_rev < 3) {
|
|
CSR_WRITE_2(sc, 0x60e, 0);
|
|
CSR_WRITE_2(sc, 0x610, 0x8000);
|
|
CSR_WRITE_2(sc, 0x604, 0);
|
|
CSR_WRITE_2(sc, 0x606, 0x200);
|
|
} else {
|
|
CSR_WRITE_4(sc, 0x188, 0x80000000);
|
|
CSR_WRITE_4(sc, 0x18c, 0x2000000);
|
|
}
|
|
|
|
/*
|
|
* Initialize TX/RX interrupts' mask
|
|
*/
|
|
CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, BWI_INTR_TIMER1);
|
|
for (i = 0; i < BWI_TXRX_NRING; ++i) {
|
|
uint32_t intrs;
|
|
|
|
if (BWI_TXRX_IS_RX(i))
|
|
intrs = BWI_TXRX_RX_INTRS;
|
|
else
|
|
intrs = BWI_TXRX_TX_INTRS;
|
|
CSR_WRITE_4(sc, BWI_TXRX_INTR_MASK(i), intrs);
|
|
}
|
|
|
|
/* allow the MAC to control the PHY clock (dynamic on/off) */
|
|
CSR_SETBITS_4(sc, BWI_STATE_LO, 0x100000);
|
|
|
|
/* Setup MAC power up delay */
|
|
CSR_WRITE_2(sc, BWI_MAC_POWERUP_DELAY, sc->sc_pwron_delay);
|
|
|
|
/* Set MAC regwin revision */
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_MACREV, mac->mac_rev);
|
|
|
|
/*
|
|
* Initialize host flags
|
|
*/
|
|
bwi_mac_hostflags_init(mac);
|
|
|
|
/*
|
|
* Initialize BSS parameters
|
|
*/
|
|
bwi_mac_bss_param_init(mac);
|
|
|
|
/*
|
|
* Initialize TX rings
|
|
*/
|
|
for (i = 0; i < BWI_TX_NRING; ++i) {
|
|
error = sc->sc_init_tx_ring(sc, i);
|
|
if (error) {
|
|
device_printf(sc->sc_dev,
|
|
"can't initialize %dth TX ring\n", i);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize RX ring
|
|
*/
|
|
error = sc->sc_init_rx_ring(sc);
|
|
if (error) {
|
|
device_printf(sc->sc_dev, "can't initialize RX ring\n");
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Initialize TX stats if the current MAC uses that
|
|
*/
|
|
if (mac->mac_flags & BWI_MAC_F_HAS_TXSTATS) {
|
|
error = sc->sc_init_txstats(sc);
|
|
if (error) {
|
|
device_printf(sc->sc_dev,
|
|
"can't initialize TX stats ring\n");
|
|
return error;
|
|
}
|
|
}
|
|
|
|
/* update PRETBTT */
|
|
CSR_WRITE_2(sc, 0x612, 0x50); /* Force Pre-TBTT to 80? */
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, 0x416, 0x50);
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, 0x414, 0x1f4);
|
|
|
|
mac->mac_flags |= BWI_MAC_F_INITED;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
bwi_mac_reset(struct bwi_mac *mac, int link_phy)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
uint32_t flags, state_lo, status;
|
|
|
|
flags = BWI_STATE_LO_FLAG_PHYRST | BWI_STATE_LO_FLAG_PHYCLKEN;
|
|
if (link_phy)
|
|
flags |= BWI_STATE_LO_FLAG_PHYLNK;
|
|
bwi_regwin_enable(sc, &mac->mac_regwin, flags);
|
|
DELAY(2000);
|
|
|
|
state_lo = CSR_READ_4(sc, BWI_STATE_LO);
|
|
state_lo |= BWI_STATE_LO_GATED_CLOCK;
|
|
state_lo &= ~__SHIFTIN(BWI_STATE_LO_FLAG_PHYRST,
|
|
BWI_STATE_LO_FLAGS_MASK);
|
|
CSR_WRITE_4(sc, BWI_STATE_LO, state_lo);
|
|
/* Flush pending bus write */
|
|
CSR_READ_4(sc, BWI_STATE_LO);
|
|
DELAY(1000);
|
|
|
|
state_lo &= ~BWI_STATE_LO_GATED_CLOCK;
|
|
CSR_WRITE_4(sc, BWI_STATE_LO, state_lo);
|
|
/* Flush pending bus write */
|
|
CSR_READ_4(sc, BWI_STATE_LO);
|
|
DELAY(1000);
|
|
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0);
|
|
|
|
status = CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
status |= BWI_MAC_STATUS_IHREN;
|
|
if (link_phy)
|
|
status |= BWI_MAC_STATUS_PHYLNK;
|
|
else
|
|
status &= ~BWI_MAC_STATUS_PHYLNK;
|
|
CSR_WRITE_4(sc, BWI_MAC_STATUS, status);
|
|
|
|
if (link_phy) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH | BWI_DBG_INIT,
|
|
"%s\n", "PHY is linked");
|
|
mac->mac_phy.phy_flags |= BWI_PHY_F_LINKED;
|
|
} else {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH | BWI_DBG_INIT,
|
|
"%s\n", "PHY is unlinked");
|
|
mac->mac_phy.phy_flags &= ~BWI_PHY_F_LINKED;
|
|
}
|
|
}
|
|
|
|
void
|
|
bwi_mac_set_tpctl_11bg(struct bwi_mac *mac, const struct bwi_tpctl *new_tpctl)
|
|
{
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
struct bwi_tpctl *tpctl = &mac->mac_tpctl;
|
|
|
|
if (new_tpctl != NULL) {
|
|
KASSERT(new_tpctl->bbp_atten <= BWI_BBP_ATTEN_MAX,
|
|
("bbp_atten %d", new_tpctl->bbp_atten));
|
|
KASSERT(new_tpctl->rf_atten <=
|
|
(rf->rf_rev < 6 ? BWI_RF_ATTEN_MAX0
|
|
: BWI_RF_ATTEN_MAX1),
|
|
("rf_atten %d", new_tpctl->rf_atten));
|
|
KASSERT(new_tpctl->tp_ctrl1 <= BWI_TPCTL1_MAX,
|
|
("tp_ctrl1 %d", new_tpctl->tp_ctrl1));
|
|
|
|
tpctl->bbp_atten = new_tpctl->bbp_atten;
|
|
tpctl->rf_atten = new_tpctl->rf_atten;
|
|
tpctl->tp_ctrl1 = new_tpctl->tp_ctrl1;
|
|
}
|
|
|
|
/* Set BBP attenuation */
|
|
bwi_phy_set_bbp_atten(mac, tpctl->bbp_atten);
|
|
|
|
/* Set RF attenuation */
|
|
RF_WRITE(mac, BWI_RFR_ATTEN, tpctl->rf_atten);
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_RF_ATTEN,
|
|
tpctl->rf_atten);
|
|
|
|
/* Set TX power */
|
|
if (rf->rf_type == BWI_RF_T_BCM2050) {
|
|
RF_FILT_SETBITS(mac, BWI_RFR_TXPWR, ~BWI_RFR_TXPWR1_MASK,
|
|
__SHIFTIN(tpctl->tp_ctrl1, BWI_RFR_TXPWR1_MASK));
|
|
}
|
|
|
|
/* Adjust RF Local Oscillator */
|
|
if (mac->mac_phy.phy_mode == IEEE80211_MODE_11G)
|
|
bwi_rf_lo_adjust(mac, tpctl);
|
|
}
|
|
|
|
static int
|
|
bwi_mac_test(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
uint32_t orig_val, val;
|
|
|
|
#define TEST_VAL1 0xaa5555aa
|
|
#define TEST_VAL2 0x55aaaa55
|
|
|
|
/* Save it for later restoring */
|
|
orig_val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0);
|
|
|
|
/* Test 1 */
|
|
MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, TEST_VAL1);
|
|
val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0);
|
|
if (val != TEST_VAL1) {
|
|
device_printf(sc->sc_dev, "TEST1 failed\n");
|
|
return ENXIO;
|
|
}
|
|
|
|
/* Test 2 */
|
|
MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, TEST_VAL2);
|
|
val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0);
|
|
if (val != TEST_VAL2) {
|
|
device_printf(sc->sc_dev, "TEST2 failed\n");
|
|
return ENXIO;
|
|
}
|
|
|
|
/* Restore to the original value */
|
|
MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, orig_val);
|
|
|
|
val = CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
if ((val & ~BWI_MAC_STATUS_PHYLNK) != BWI_MAC_STATUS_IHREN) {
|
|
device_printf(sc->sc_dev, "%s failed, MAC status 0x%08x\n",
|
|
__func__, val);
|
|
return ENXIO;
|
|
}
|
|
|
|
val = CSR_READ_4(sc, BWI_MAC_INTR_STATUS);
|
|
if (val != 0) {
|
|
device_printf(sc->sc_dev, "%s failed, intr status %08x\n",
|
|
__func__, val);
|
|
return ENXIO;
|
|
}
|
|
|
|
#undef TEST_VAL2
|
|
#undef TEST_VAL1
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
bwi_mac_setup_tpctl(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
struct bwi_tpctl *tpctl = &mac->mac_tpctl;
|
|
|
|
/* Calc BBP attenuation */
|
|
if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev < 6)
|
|
tpctl->bbp_atten = 0;
|
|
else
|
|
tpctl->bbp_atten = 2;
|
|
|
|
/* Calc TX power CTRL1?? */
|
|
tpctl->tp_ctrl1 = 0;
|
|
if (rf->rf_type == BWI_RF_T_BCM2050) {
|
|
if (rf->rf_rev == 1)
|
|
tpctl->tp_ctrl1 = 3;
|
|
else if (rf->rf_rev < 6)
|
|
tpctl->tp_ctrl1 = 2;
|
|
else if (rf->rf_rev == 8)
|
|
tpctl->tp_ctrl1 = 1;
|
|
}
|
|
|
|
/* Empty TX power CTRL2?? */
|
|
tpctl->tp_ctrl2 = 0xffff;
|
|
|
|
/*
|
|
* Calc RF attenuation
|
|
*/
|
|
if (phy->phy_mode == IEEE80211_MODE_11A) {
|
|
tpctl->rf_atten = 0x60;
|
|
goto back;
|
|
}
|
|
|
|
if (BWI_IS_BRCM_BCM4309G(sc) && sc->sc_pci_revid < 0x51) {
|
|
tpctl->rf_atten = sc->sc_pci_revid < 0x43 ? 2 : 3;
|
|
goto back;
|
|
}
|
|
|
|
tpctl->rf_atten = 5;
|
|
|
|
if (rf->rf_type != BWI_RF_T_BCM2050) {
|
|
if (rf->rf_type == BWI_RF_T_BCM2053 && rf->rf_rev == 1)
|
|
tpctl->rf_atten = 6;
|
|
goto back;
|
|
}
|
|
|
|
/*
|
|
* NB: If we reaches here and the card is BRCM_BCM4309G,
|
|
* then the card's PCI revision must >= 0x51
|
|
*/
|
|
|
|
/* BCM2050 RF */
|
|
switch (rf->rf_rev) {
|
|
case 1:
|
|
if (phy->phy_mode == IEEE80211_MODE_11G) {
|
|
if (BWI_IS_BRCM_BCM4309G(sc) || BWI_IS_BRCM_BU4306(sc))
|
|
tpctl->rf_atten = 3;
|
|
else
|
|
tpctl->rf_atten = 1;
|
|
} else {
|
|
if (BWI_IS_BRCM_BCM4309G(sc))
|
|
tpctl->rf_atten = 7;
|
|
else
|
|
tpctl->rf_atten = 6;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (phy->phy_mode == IEEE80211_MODE_11G) {
|
|
/*
|
|
* NOTE: Order of following conditions is critical
|
|
*/
|
|
if (BWI_IS_BRCM_BCM4309G(sc))
|
|
tpctl->rf_atten = 3;
|
|
else if (BWI_IS_BRCM_BU4306(sc))
|
|
tpctl->rf_atten = 5;
|
|
else if (sc->sc_bbp_id == BWI_BBPID_BCM4320)
|
|
tpctl->rf_atten = 4;
|
|
else
|
|
tpctl->rf_atten = 3;
|
|
} else {
|
|
tpctl->rf_atten = 6;
|
|
}
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
tpctl->rf_atten = 1;
|
|
break;
|
|
case 8:
|
|
tpctl->rf_atten = 0x1a;
|
|
break;
|
|
}
|
|
back:
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_INIT | BWI_DBG_TXPOWER,
|
|
"bbp atten: %u, rf atten: %u, ctrl1: %u, ctrl2: %u\n",
|
|
tpctl->bbp_atten, tpctl->rf_atten,
|
|
tpctl->tp_ctrl1, tpctl->tp_ctrl2);
|
|
}
|
|
|
|
void
|
|
bwi_mac_dummy_xmit(struct bwi_mac *mac)
|
|
{
|
|
#define PACKET_LEN 5
|
|
static const uint32_t packet_11a[PACKET_LEN] =
|
|
{ 0x000201cc, 0x00d40000, 0x00000000, 0x01000000, 0x00000000 };
|
|
static const uint32_t packet_11bg[PACKET_LEN] =
|
|
{ 0x000b846e, 0x00d40000, 0x00000000, 0x01000000, 0x00000000 };
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
const uint32_t *packet;
|
|
uint16_t val_50c;
|
|
int wait_max, i;
|
|
|
|
if (mac->mac_phy.phy_mode == IEEE80211_MODE_11A) {
|
|
wait_max = 30;
|
|
packet = packet_11a;
|
|
val_50c = 1;
|
|
} else {
|
|
wait_max = 250;
|
|
packet = packet_11bg;
|
|
val_50c = 0;
|
|
}
|
|
|
|
for (i = 0; i < PACKET_LEN; ++i)
|
|
TMPLT_WRITE_4(mac, i * 4, packet[i]);
|
|
|
|
CSR_READ_4(sc, BWI_MAC_STATUS); /* dummy read */
|
|
|
|
CSR_WRITE_2(sc, 0x568, 0);
|
|
CSR_WRITE_2(sc, 0x7c0, 0);
|
|
CSR_WRITE_2(sc, 0x50c, val_50c);
|
|
CSR_WRITE_2(sc, 0x508, 0);
|
|
CSR_WRITE_2(sc, 0x50a, 0);
|
|
CSR_WRITE_2(sc, 0x54c, 0);
|
|
CSR_WRITE_2(sc, 0x56a, 0x14);
|
|
CSR_WRITE_2(sc, 0x568, 0x826);
|
|
CSR_WRITE_2(sc, 0x500, 0);
|
|
CSR_WRITE_2(sc, 0x502, 0x30);
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev <= 5)
|
|
RF_WRITE(mac, 0x51, 0x17);
|
|
|
|
for (i = 0; i < wait_max; ++i) {
|
|
if (CSR_READ_2(sc, 0x50e) & 0x80)
|
|
break;
|
|
DELAY(10);
|
|
}
|
|
for (i = 0; i < 10; ++i) {
|
|
if (CSR_READ_2(sc, 0x50e) & 0x400)
|
|
break;
|
|
DELAY(10);
|
|
}
|
|
for (i = 0; i < 10; ++i) {
|
|
if ((CSR_READ_2(sc, 0x690) & 0x100) == 0)
|
|
break;
|
|
DELAY(10);
|
|
}
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev <= 5)
|
|
RF_WRITE(mac, 0x51, 0x37);
|
|
#undef PACKET_LEN
|
|
}
|
|
|
|
void
|
|
bwi_mac_init_tpctl_11bg(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
struct bwi_tpctl tpctl_orig;
|
|
int restore_tpctl = 0;
|
|
|
|
KASSERT(phy->phy_mode != IEEE80211_MODE_11A,
|
|
("phy_mode %d", phy->phy_mode));
|
|
|
|
if (BWI_IS_BRCM_BU4306(sc))
|
|
return;
|
|
|
|
PHY_WRITE(mac, 0x28, 0x8018);
|
|
CSR_CLRBITS_2(sc, BWI_BBP_ATTEN, 0x20);
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11G) {
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) == 0)
|
|
return;
|
|
PHY_WRITE(mac, 0x47a, 0xc111);
|
|
}
|
|
if (mac->mac_flags & BWI_MAC_F_TPCTL_INITED)
|
|
return;
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11B && phy->phy_rev >= 2 &&
|
|
rf->rf_type == BWI_RF_T_BCM2050) {
|
|
RF_SETBITS(mac, 0x76, 0x84);
|
|
} else {
|
|
struct bwi_tpctl tpctl;
|
|
|
|
/* Backup original TX power control variables */
|
|
bcopy(&mac->mac_tpctl, &tpctl_orig, sizeof(tpctl_orig));
|
|
restore_tpctl = 1;
|
|
|
|
bcopy(&mac->mac_tpctl, &tpctl, sizeof(tpctl));
|
|
tpctl.bbp_atten = 11;
|
|
tpctl.tp_ctrl1 = 0;
|
|
#ifdef notyet
|
|
if (rf->rf_rev >= 6 && rf->rf_rev <= 8)
|
|
tpctl.rf_atten = 31;
|
|
else
|
|
#endif
|
|
tpctl.rf_atten = 9;
|
|
|
|
bwi_mac_set_tpctl_11bg(mac, &tpctl);
|
|
}
|
|
|
|
bwi_mac_dummy_xmit(mac);
|
|
|
|
mac->mac_flags |= BWI_MAC_F_TPCTL_INITED;
|
|
rf->rf_base_tssi = PHY_READ(mac, 0x29);
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_INIT | BWI_DBG_TXPOWER,
|
|
"base tssi %d\n", rf->rf_base_tssi);
|
|
|
|
if (abs(rf->rf_base_tssi - rf->rf_idle_tssi) >= 20) {
|
|
device_printf(sc->sc_dev, "base tssi measure failed\n");
|
|
mac->mac_flags |= BWI_MAC_F_TPCTL_ERROR;
|
|
}
|
|
|
|
if (restore_tpctl)
|
|
bwi_mac_set_tpctl_11bg(mac, &tpctl_orig);
|
|
else
|
|
RF_CLRBITS(mac, 0x76, 0x84);
|
|
|
|
bwi_rf_clear_tssi(mac);
|
|
}
|
|
|
|
void
|
|
bwi_mac_detach(struct bwi_mac *mac)
|
|
{
|
|
bwi_mac_fw_free(mac);
|
|
}
|
|
|
|
static __inline int
|
|
bwi_fwimage_is_valid(struct bwi_softc *sc, const struct firmware *fw,
|
|
uint8_t fw_type)
|
|
{
|
|
const struct bwi_fwhdr *hdr;
|
|
|
|
if (fw->datasize < sizeof(*hdr)) {
|
|
device_printf(sc->sc_dev,
|
|
"invalid firmware (%s): invalid size %zu\n",
|
|
fw->name, fw->datasize);
|
|
return 0;
|
|
}
|
|
|
|
hdr = (const struct bwi_fwhdr *)fw->data;
|
|
|
|
if (fw_type != BWI_FW_T_IV) {
|
|
/*
|
|
* Don't verify IV's size, it has different meaning
|
|
*/
|
|
if (be32toh(hdr->fw_size) != fw->datasize - sizeof(*hdr)) {
|
|
device_printf(sc->sc_dev,
|
|
"invalid firmware (%s): size mismatch, "
|
|
"fw %u, real %zu\n", fw->name,
|
|
be32toh(hdr->fw_size), fw->datasize - sizeof(*hdr));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (hdr->fw_type != fw_type) {
|
|
device_printf(sc->sc_dev,
|
|
"invalid firmware (%s): type mismatch, "
|
|
"fw \'%c\', target \'%c\'\n", fw->name,
|
|
hdr->fw_type, fw_type);
|
|
return 0;
|
|
}
|
|
|
|
if (hdr->fw_gen != BWI_FW_GEN_1) {
|
|
device_printf(sc->sc_dev,
|
|
"invalid firmware (%s): wrong generation, "
|
|
"fw %d, target %d\n", fw->name, hdr->fw_gen, BWI_FW_GEN_1);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* XXX Error cleanup
|
|
*/
|
|
int
|
|
bwi_mac_fw_alloc(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
char fwname[64];
|
|
int idx;
|
|
|
|
/*
|
|
* Try getting the firmware stub so firmware
|
|
* module would be loaded automatically
|
|
*/
|
|
if (mac->mac_stub == NULL) {
|
|
snprintf(fwname, sizeof(fwname), BWI_FW_STUB_PATH,
|
|
sc->sc_fw_version);
|
|
mac->mac_stub = firmware_get(fwname);
|
|
if (mac->mac_stub == NULL)
|
|
goto no_firmware;
|
|
}
|
|
|
|
if (mac->mac_ucode == NULL) {
|
|
snprintf(fwname, sizeof(fwname), BWI_FW_UCODE_PATH,
|
|
sc->sc_fw_version,
|
|
mac->mac_rev >= 5 ? 5 : mac->mac_rev);
|
|
|
|
mac->mac_ucode = firmware_get(fwname);
|
|
if (mac->mac_ucode == NULL)
|
|
goto no_firmware;
|
|
if (!bwi_fwimage_is_valid(sc, mac->mac_ucode, BWI_FW_T_UCODE))
|
|
return EINVAL;
|
|
}
|
|
|
|
if (mac->mac_pcm == NULL) {
|
|
snprintf(fwname, sizeof(fwname), BWI_FW_PCM_PATH,
|
|
sc->sc_fw_version,
|
|
mac->mac_rev < 5 ? 4 : 5);
|
|
|
|
mac->mac_pcm = firmware_get(fwname);
|
|
if (mac->mac_pcm == NULL)
|
|
goto no_firmware;
|
|
if (!bwi_fwimage_is_valid(sc, mac->mac_pcm, BWI_FW_T_PCM))
|
|
return EINVAL;
|
|
}
|
|
|
|
if (mac->mac_iv == NULL) {
|
|
/* TODO: 11A */
|
|
if (mac->mac_rev == 2 || mac->mac_rev == 4) {
|
|
idx = 2;
|
|
} else if (mac->mac_rev >= 5 && mac->mac_rev <= 10) {
|
|
idx = 5;
|
|
} else {
|
|
device_printf(sc->sc_dev,
|
|
"no suitible IV for MAC rev %d\n", mac->mac_rev);
|
|
return ENODEV;
|
|
}
|
|
|
|
snprintf(fwname, sizeof(fwname), BWI_FW_IV_PATH,
|
|
sc->sc_fw_version, idx);
|
|
|
|
mac->mac_iv = firmware_get(fwname);
|
|
if (mac->mac_iv == NULL)
|
|
goto no_firmware;
|
|
if (!bwi_fwimage_is_valid(sc, mac->mac_iv, BWI_FW_T_IV))
|
|
return EINVAL;
|
|
}
|
|
|
|
if (mac->mac_iv_ext == NULL) {
|
|
/* TODO: 11A */
|
|
if (mac->mac_rev == 2 || mac->mac_rev == 4 ||
|
|
mac->mac_rev >= 11) {
|
|
/* No extended IV */
|
|
return (0);
|
|
} else if (mac->mac_rev >= 5 && mac->mac_rev <= 10) {
|
|
idx = 5;
|
|
} else {
|
|
device_printf(sc->sc_dev,
|
|
"no suitible ExtIV for MAC rev %d\n", mac->mac_rev);
|
|
return ENODEV;
|
|
}
|
|
|
|
snprintf(fwname, sizeof(fwname), BWI_FW_IV_EXT_PATH,
|
|
sc->sc_fw_version, idx);
|
|
|
|
mac->mac_iv_ext = firmware_get(fwname);
|
|
if (mac->mac_iv_ext == NULL)
|
|
goto no_firmware;
|
|
if (!bwi_fwimage_is_valid(sc, mac->mac_iv_ext, BWI_FW_T_IV))
|
|
return EINVAL;
|
|
}
|
|
return (0);
|
|
|
|
no_firmware:
|
|
device_printf(sc->sc_dev, "request firmware %s failed\n", fwname);
|
|
return (ENOENT);
|
|
}
|
|
|
|
static void
|
|
bwi_mac_fw_free(struct bwi_mac *mac)
|
|
{
|
|
if (mac->mac_ucode != NULL) {
|
|
firmware_put(mac->mac_ucode, FIRMWARE_UNLOAD);
|
|
mac->mac_ucode = NULL;
|
|
}
|
|
|
|
if (mac->mac_pcm != NULL) {
|
|
firmware_put(mac->mac_pcm, FIRMWARE_UNLOAD);
|
|
mac->mac_pcm = NULL;
|
|
}
|
|
|
|
if (mac->mac_iv != NULL) {
|
|
firmware_put(mac->mac_iv, FIRMWARE_UNLOAD);
|
|
mac->mac_iv = NULL;
|
|
}
|
|
|
|
if (mac->mac_iv_ext != NULL) {
|
|
firmware_put(mac->mac_iv_ext, FIRMWARE_UNLOAD);
|
|
mac->mac_iv_ext = NULL;
|
|
}
|
|
|
|
if (mac->mac_stub != NULL) {
|
|
firmware_put(mac->mac_stub, FIRMWARE_UNLOAD);
|
|
mac->mac_stub = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
bwi_mac_fw_load(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
const uint32_t *fw;
|
|
uint16_t fw_rev;
|
|
int fw_len, i;
|
|
|
|
/*
|
|
* Load ucode image
|
|
*/
|
|
fw = (const uint32_t *)
|
|
((const uint8_t *)mac->mac_ucode->data + BWI_FWHDR_SZ);
|
|
fw_len = (mac->mac_ucode->datasize - BWI_FWHDR_SZ) / sizeof(uint32_t);
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
|
|
BWI_MOBJ_CTRL_VAL(
|
|
BWI_FW_UCODE_MOBJ | BWI_WR_MOBJ_AUTOINC, 0));
|
|
for (i = 0; i < fw_len; ++i) {
|
|
CSR_WRITE_4(sc, BWI_MOBJ_DATA, be32toh(fw[i]));
|
|
DELAY(10);
|
|
}
|
|
|
|
/*
|
|
* Load PCM image
|
|
*/
|
|
fw = (const uint32_t *)
|
|
((const uint8_t *)mac->mac_pcm->data + BWI_FWHDR_SZ);
|
|
fw_len = (mac->mac_pcm->datasize - BWI_FWHDR_SZ) / sizeof(uint32_t);
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
|
|
BWI_MOBJ_CTRL_VAL(BWI_FW_PCM_MOBJ, 0x01ea));
|
|
CSR_WRITE_4(sc, BWI_MOBJ_DATA, 0x4000);
|
|
|
|
CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
|
|
BWI_MOBJ_CTRL_VAL(BWI_FW_PCM_MOBJ, 0x01eb));
|
|
for (i = 0; i < fw_len; ++i) {
|
|
CSR_WRITE_4(sc, BWI_MOBJ_DATA, be32toh(fw[i]));
|
|
DELAY(10);
|
|
}
|
|
|
|
CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, BWI_ALL_INTRS);
|
|
CSR_WRITE_4(sc, BWI_MAC_STATUS,
|
|
BWI_MAC_STATUS_UCODE_START |
|
|
BWI_MAC_STATUS_IHREN |
|
|
BWI_MAC_STATUS_INFRA);
|
|
|
|
#define NRETRY 200
|
|
|
|
for (i = 0; i < NRETRY; ++i) {
|
|
uint32_t intr_status;
|
|
|
|
intr_status = CSR_READ_4(sc, BWI_MAC_INTR_STATUS);
|
|
if (intr_status == BWI_INTR_READY)
|
|
break;
|
|
DELAY(10);
|
|
}
|
|
if (i == NRETRY) {
|
|
device_printf(sc->sc_dev,
|
|
"firmware (ucode&pcm) loading timed out\n");
|
|
return ETIMEDOUT;
|
|
}
|
|
|
|
#undef NRETRY
|
|
|
|
CSR_READ_4(sc, BWI_MAC_INTR_STATUS); /* dummy read */
|
|
|
|
fw_rev = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_FWREV);
|
|
if (fw_rev > BWI_FW_VERSION3_REVMAX) {
|
|
device_printf(sc->sc_dev,
|
|
"firmware version 4 is not supported yet\n");
|
|
return ENODEV;
|
|
}
|
|
|
|
device_printf(sc->sc_dev,
|
|
"firmware rev 0x%04x, patch level 0x%04x\n", fw_rev,
|
|
MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_FWPATCHLV));
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bwi_mac_gpio_init(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_regwin *old, *gpio_rw;
|
|
uint32_t filt, bits;
|
|
int error;
|
|
|
|
CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_GPOSEL_MASK);
|
|
/* TODO:LED */
|
|
|
|
CSR_SETBITS_2(sc, BWI_MAC_GPIO_MASK, 0xf);
|
|
|
|
filt = 0x1f;
|
|
bits = 0xf;
|
|
if (sc->sc_bbp_id == BWI_BBPID_BCM4301) {
|
|
filt |= 0x60;
|
|
bits |= 0x60;
|
|
}
|
|
if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) {
|
|
CSR_SETBITS_2(sc, BWI_MAC_GPIO_MASK, 0x200);
|
|
filt |= 0x200;
|
|
bits |= 0x200;
|
|
}
|
|
|
|
gpio_rw = BWI_GPIO_REGWIN(sc);
|
|
error = bwi_regwin_switch(sc, gpio_rw, &old);
|
|
if (error)
|
|
return error;
|
|
|
|
CSR_FILT_SETBITS_4(sc, BWI_GPIO_CTRL, filt, bits);
|
|
|
|
return bwi_regwin_switch(sc, old, NULL);
|
|
}
|
|
|
|
static int
|
|
bwi_mac_gpio_fini(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_regwin *old, *gpio_rw;
|
|
int error;
|
|
|
|
gpio_rw = BWI_GPIO_REGWIN(sc);
|
|
error = bwi_regwin_switch(sc, gpio_rw, &old);
|
|
if (error)
|
|
return error;
|
|
|
|
CSR_WRITE_4(sc, BWI_GPIO_CTRL, 0);
|
|
|
|
return bwi_regwin_switch(sc, old, NULL);
|
|
}
|
|
|
|
static int
|
|
bwi_mac_fw_load_iv(struct bwi_mac *mac, const struct firmware *fw)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
const struct bwi_fwhdr *hdr;
|
|
const struct bwi_fw_iv *iv;
|
|
int n, i, iv_img_size;
|
|
|
|
/* Get the number of IVs in the IV image */
|
|
hdr = (const struct bwi_fwhdr *)fw->data;
|
|
n = be32toh(hdr->fw_iv_cnt);
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_INIT | BWI_DBG_FIRMWARE,
|
|
"IV count %d\n", n);
|
|
|
|
/* Calculate the IV image size, for later sanity check */
|
|
iv_img_size = fw->datasize - sizeof(*hdr);
|
|
|
|
/* Locate the first IV */
|
|
iv = (const struct bwi_fw_iv *)
|
|
((const uint8_t *)fw->data + sizeof(*hdr));
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
uint16_t iv_ofs, ofs;
|
|
int sz = 0;
|
|
|
|
if (iv_img_size < sizeof(iv->iv_ofs)) {
|
|
device_printf(sc->sc_dev, "invalid IV image, ofs\n");
|
|
return EINVAL;
|
|
}
|
|
iv_img_size -= sizeof(iv->iv_ofs);
|
|
sz += sizeof(iv->iv_ofs);
|
|
|
|
iv_ofs = be16toh(iv->iv_ofs);
|
|
|
|
ofs = __SHIFTOUT(iv_ofs, BWI_FW_IV_OFS_MASK);
|
|
if (ofs >= 0x1000) {
|
|
device_printf(sc->sc_dev, "invalid ofs (0x%04x) "
|
|
"for %dth iv\n", ofs, i);
|
|
return EINVAL;
|
|
}
|
|
|
|
if (iv_ofs & BWI_FW_IV_IS_32BIT) {
|
|
uint32_t val32;
|
|
|
|
if (iv_img_size < sizeof(iv->iv_val.val32)) {
|
|
device_printf(sc->sc_dev,
|
|
"invalid IV image, val32\n");
|
|
return EINVAL;
|
|
}
|
|
iv_img_size -= sizeof(iv->iv_val.val32);
|
|
sz += sizeof(iv->iv_val.val32);
|
|
|
|
val32 = be32toh(iv->iv_val.val32);
|
|
CSR_WRITE_4(sc, ofs, val32);
|
|
} else {
|
|
uint16_t val16;
|
|
|
|
if (iv_img_size < sizeof(iv->iv_val.val16)) {
|
|
device_printf(sc->sc_dev,
|
|
"invalid IV image, val16\n");
|
|
return EINVAL;
|
|
}
|
|
iv_img_size -= sizeof(iv->iv_val.val16);
|
|
sz += sizeof(iv->iv_val.val16);
|
|
|
|
val16 = be16toh(iv->iv_val.val16);
|
|
CSR_WRITE_2(sc, ofs, val16);
|
|
}
|
|
|
|
iv = (const struct bwi_fw_iv *)((const uint8_t *)iv + sz);
|
|
}
|
|
|
|
if (iv_img_size != 0) {
|
|
device_printf(sc->sc_dev, "invalid IV image, size left %d\n",
|
|
iv_img_size);
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bwi_mac_fw_init(struct bwi_mac *mac)
|
|
{
|
|
device_t dev = mac->mac_sc->sc_dev;
|
|
int error;
|
|
|
|
error = bwi_mac_fw_load_iv(mac, mac->mac_iv);
|
|
if (error) {
|
|
device_printf(dev, "load IV failed\n");
|
|
return error;
|
|
}
|
|
|
|
if (mac->mac_iv_ext != NULL) {
|
|
error = bwi_mac_fw_load_iv(mac, mac->mac_iv_ext);
|
|
if (error)
|
|
device_printf(dev, "load ExtIV failed\n");
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
bwi_mac_opmode_init(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
uint32_t mac_status;
|
|
uint16_t pre_tbtt;
|
|
|
|
CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_INFRA);
|
|
CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_INFRA);
|
|
|
|
/* Set probe resp timeout to infinite */
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_PROBE_RESP_TO, 0);
|
|
|
|
/*
|
|
* TODO: factor out following part
|
|
*/
|
|
|
|
mac_status = CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
mac_status &= ~(BWI_MAC_STATUS_OPMODE_HOSTAP |
|
|
BWI_MAC_STATUS_PASS_CTL |
|
|
BWI_MAC_STATUS_PASS_BCN |
|
|
BWI_MAC_STATUS_PASS_BADPLCP |
|
|
BWI_MAC_STATUS_PASS_BADFCS |
|
|
BWI_MAC_STATUS_PROMISC);
|
|
mac_status |= BWI_MAC_STATUS_INFRA;
|
|
|
|
/* Always turn on PROMISC on old hardware */
|
|
if (mac->mac_rev < 5)
|
|
mac_status |= BWI_MAC_STATUS_PROMISC;
|
|
|
|
switch (ic->ic_opmode) {
|
|
case IEEE80211_M_IBSS:
|
|
mac_status &= ~BWI_MAC_STATUS_INFRA;
|
|
break;
|
|
case IEEE80211_M_HOSTAP:
|
|
mac_status |= BWI_MAC_STATUS_OPMODE_HOSTAP;
|
|
break;
|
|
case IEEE80211_M_MONITOR:
|
|
#if 0
|
|
/* Do you want data from your microwave oven? */
|
|
mac_status |= BWI_MAC_STATUS_PASS_CTL |
|
|
BWI_MAC_STATUS_PASS_BADPLCP |
|
|
BWI_MAC_STATUS_PASS_BADFCS;
|
|
#else
|
|
mac_status |= BWI_MAC_STATUS_PASS_CTL;
|
|
#endif
|
|
/* Promisc? */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ic->ic_promisc > 0)
|
|
mac_status |= BWI_MAC_STATUS_PROMISC;
|
|
|
|
CSR_WRITE_4(sc, BWI_MAC_STATUS, mac_status);
|
|
|
|
if (ic->ic_opmode != IEEE80211_M_IBSS &&
|
|
ic->ic_opmode != IEEE80211_M_HOSTAP) {
|
|
if (sc->sc_bbp_id == BWI_BBPID_BCM4306 && sc->sc_bbp_rev == 3)
|
|
pre_tbtt = 100;
|
|
else
|
|
pre_tbtt = 50;
|
|
} else {
|
|
pre_tbtt = 2;
|
|
}
|
|
CSR_WRITE_2(sc, BWI_MAC_PRE_TBTT, pre_tbtt);
|
|
}
|
|
|
|
static void
|
|
bwi_mac_hostflags_init(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
uint64_t host_flags;
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A)
|
|
return;
|
|
|
|
host_flags = HFLAGS_READ(mac);
|
|
host_flags |= BWI_HFLAG_SYM_WA;
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11G) {
|
|
if (phy->phy_rev == 1)
|
|
host_flags |= BWI_HFLAG_GDC_WA;
|
|
if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9)
|
|
host_flags |= BWI_HFLAG_OFDM_PA;
|
|
} else if (phy->phy_mode == IEEE80211_MODE_11B) {
|
|
if (phy->phy_rev >= 2 && rf->rf_type == BWI_RF_T_BCM2050)
|
|
host_flags &= ~BWI_HFLAG_GDC_WA;
|
|
} else {
|
|
panic("unknown PHY mode %u\n", phy->phy_mode);
|
|
}
|
|
|
|
HFLAGS_WRITE(mac, host_flags);
|
|
}
|
|
|
|
static void
|
|
bwi_mac_bss_param_init(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
const struct ieee80211_rate_table *rt;
|
|
struct bwi_retry_lim lim;
|
|
uint16_t cw_min;
|
|
|
|
/*
|
|
* Set short/long retry limits
|
|
*/
|
|
bzero(&lim, sizeof(lim));
|
|
lim.shretry = BWI_SHRETRY;
|
|
lim.shretry_fb = BWI_SHRETRY_FB;
|
|
lim.lgretry = BWI_LGRETRY;
|
|
lim.lgretry_fb = BWI_LGRETRY_FB;
|
|
bwi_mac_set_retry_lim(mac, &lim);
|
|
|
|
/*
|
|
* Implicitly prevent firmware from sending probe response
|
|
* by setting its "probe response timeout" to 1us.
|
|
*/
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_PROBE_RESP_TO, 1);
|
|
|
|
/*
|
|
* XXX MAC level acknowledge and CW min/max should depend
|
|
* on the char rateset of the IBSS/BSS to join.
|
|
* XXX this is all wrong; should be done on channel change
|
|
*/
|
|
if (phy->phy_mode == IEEE80211_MODE_11B) {
|
|
rt = ieee80211_get_ratetable(
|
|
ieee80211_find_channel(ic, 2412, IEEE80211_CHAN_B));
|
|
bwi_mac_set_ackrates(mac, rt,
|
|
&ic->ic_sup_rates[IEEE80211_MODE_11B]);
|
|
} else {
|
|
rt = ieee80211_get_ratetable(
|
|
ieee80211_find_channel(ic, 2412, IEEE80211_CHAN_G));
|
|
bwi_mac_set_ackrates(mac, rt,
|
|
&ic->ic_sup_rates[IEEE80211_MODE_11G]);
|
|
}
|
|
|
|
/*
|
|
* Set CW min
|
|
*/
|
|
if (phy->phy_mode == IEEE80211_MODE_11B)
|
|
cw_min = IEEE80211_CW_MIN_0;
|
|
else
|
|
cw_min = IEEE80211_CW_MIN_1;
|
|
MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_CWMIN, cw_min);
|
|
|
|
/*
|
|
* Set CW max
|
|
*/
|
|
MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_CWMAX,
|
|
IEEE80211_CW_MAX);
|
|
}
|
|
|
|
static void
|
|
bwi_mac_set_retry_lim(struct bwi_mac *mac, const struct bwi_retry_lim *lim)
|
|
{
|
|
/* Short/Long retry limit */
|
|
MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_SHRETRY,
|
|
lim->shretry);
|
|
MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_LGRETRY,
|
|
lim->lgretry);
|
|
|
|
/* Short/Long retry fallback limit */
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_SHRETRY_FB,
|
|
lim->shretry_fb);
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_LGRETEY_FB,
|
|
lim->lgretry_fb);
|
|
}
|
|
|
|
static void
|
|
bwi_mac_set_ackrates(struct bwi_mac *mac, const struct ieee80211_rate_table *rt,
|
|
const struct ieee80211_rateset *rs)
|
|
{
|
|
int i;
|
|
|
|
/* XXX not standard conforming */
|
|
for (i = 0; i < rs->rs_nrates; ++i) {
|
|
enum ieee80211_phytype modtype;
|
|
uint16_t ofs;
|
|
|
|
modtype = ieee80211_rate2phytype(rt,
|
|
rs->rs_rates[i] & IEEE80211_RATE_VAL);
|
|
switch (modtype) {
|
|
case IEEE80211_T_DS:
|
|
ofs = 0x4c0;
|
|
break;
|
|
case IEEE80211_T_OFDM:
|
|
ofs = 0x480;
|
|
break;
|
|
default:
|
|
panic("unsupported modtype %u\n", modtype);
|
|
}
|
|
ofs += 2*(ieee80211_rate2plcp(
|
|
rs->rs_rates[i] & IEEE80211_RATE_VAL,
|
|
modtype) & 0xf);
|
|
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, ofs + 0x20,
|
|
MOBJ_READ_2(mac, BWI_COMM_MOBJ, ofs));
|
|
}
|
|
}
|
|
|
|
int
|
|
bwi_mac_start(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_ENABLE);
|
|
CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, BWI_INTR_READY);
|
|
|
|
/* Flush pending bus writes */
|
|
CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
CSR_READ_4(sc, BWI_MAC_INTR_STATUS);
|
|
|
|
return bwi_mac_config_ps(mac);
|
|
}
|
|
|
|
int
|
|
bwi_mac_stop(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
int error, i;
|
|
|
|
error = bwi_mac_config_ps(mac);
|
|
if (error)
|
|
return error;
|
|
|
|
CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_ENABLE);
|
|
|
|
/* Flush pending bus write */
|
|
CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
|
|
#define NRETRY 10000
|
|
for (i = 0; i < NRETRY; ++i) {
|
|
if (CSR_READ_4(sc, BWI_MAC_INTR_STATUS) & BWI_INTR_READY)
|
|
break;
|
|
DELAY(1);
|
|
}
|
|
if (i == NRETRY) {
|
|
device_printf(sc->sc_dev, "can't stop MAC\n");
|
|
return ETIMEDOUT;
|
|
}
|
|
#undef NRETRY
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
bwi_mac_config_ps(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
uint32_t status;
|
|
|
|
status = CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
|
|
status &= ~BWI_MAC_STATUS_HW_PS;
|
|
status |= BWI_MAC_STATUS_WAKEUP;
|
|
CSR_WRITE_4(sc, BWI_MAC_STATUS, status);
|
|
|
|
/* Flush pending bus write */
|
|
CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
|
|
if (mac->mac_rev >= 5) {
|
|
int i;
|
|
|
|
#define NRETRY 100
|
|
for (i = 0; i < NRETRY; ++i) {
|
|
if (MOBJ_READ_2(mac, BWI_COMM_MOBJ,
|
|
BWI_COMM_MOBJ_UCODE_STATE) != BWI_UCODE_STATE_PS)
|
|
break;
|
|
DELAY(10);
|
|
}
|
|
if (i == NRETRY) {
|
|
device_printf(sc->sc_dev, "config PS failed\n");
|
|
return ETIMEDOUT;
|
|
}
|
|
#undef NRETRY
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
bwi_mac_reset_hwkeys(struct bwi_mac *mac)
|
|
{
|
|
/* TODO: firmware crypto */
|
|
MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_KEYTABLE_OFS);
|
|
}
|
|
|
|
void
|
|
bwi_mac_shutdown(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
int i;
|
|
|
|
if (mac->mac_flags & BWI_MAC_F_HAS_TXSTATS)
|
|
sc->sc_free_txstats(sc);
|
|
|
|
sc->sc_free_rx_ring(sc);
|
|
|
|
for (i = 0; i < BWI_TX_NRING; ++i)
|
|
sc->sc_free_tx_ring(sc, i);
|
|
|
|
bwi_rf_off(mac);
|
|
|
|
/* TODO:LED */
|
|
|
|
bwi_mac_gpio_fini(mac);
|
|
|
|
bwi_rf_off(mac); /* XXX again */
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, BWI_BBP_ATTEN_MAGIC);
|
|
bwi_regwin_disable(sc, &mac->mac_regwin, 0);
|
|
|
|
mac->mac_flags &= ~BWI_MAC_F_INITED;
|
|
}
|
|
|
|
static int
|
|
bwi_mac_get_property(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
enum bwi_bus_space old_bus_space;
|
|
uint32_t val;
|
|
|
|
/*
|
|
* Byte swap
|
|
*/
|
|
val = CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
if (val & BWI_MAC_STATUS_BSWAP) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
|
|
"need byte swap");
|
|
mac->mac_flags |= BWI_MAC_F_BSWAP;
|
|
}
|
|
|
|
/*
|
|
* DMA address space
|
|
*/
|
|
old_bus_space = sc->sc_bus_space;
|
|
|
|
val = CSR_READ_4(sc, BWI_STATE_HI);
|
|
if (__SHIFTOUT(val, BWI_STATE_HI_FLAGS_MASK) &
|
|
BWI_STATE_HI_FLAG_64BIT) {
|
|
/* 64bit address */
|
|
sc->sc_bus_space = BWI_BUS_SPACE_64BIT;
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
|
|
"64bit bus space");
|
|
} else {
|
|
uint32_t txrx_reg = BWI_TXRX_CTRL_BASE + BWI_TX32_CTRL;
|
|
|
|
CSR_WRITE_4(sc, txrx_reg, BWI_TXRX32_CTRL_ADDRHI_MASK);
|
|
if (CSR_READ_4(sc, txrx_reg) & BWI_TXRX32_CTRL_ADDRHI_MASK) {
|
|
/* 32bit address */
|
|
sc->sc_bus_space = BWI_BUS_SPACE_32BIT;
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
|
|
"32bit bus space");
|
|
} else {
|
|
/* 30bit address */
|
|
sc->sc_bus_space = BWI_BUS_SPACE_30BIT;
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
|
|
"30bit bus space");
|
|
}
|
|
}
|
|
|
|
if (old_bus_space != 0 && old_bus_space != sc->sc_bus_space) {
|
|
device_printf(sc->sc_dev, "MACs bus space mismatch!\n");
|
|
return ENXIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
bwi_mac_updateslot(struct bwi_mac *mac, int shslot)
|
|
{
|
|
uint16_t slot_time;
|
|
|
|
if (mac->mac_phy.phy_mode == IEEE80211_MODE_11B)
|
|
return;
|
|
|
|
if (shslot)
|
|
slot_time = IEEE80211_DUR_SHSLOT;
|
|
else
|
|
slot_time = IEEE80211_DUR_SLOT;
|
|
|
|
CSR_WRITE_2(mac->mac_sc, BWI_MAC_SLOTTIME,
|
|
slot_time + BWI_MAC_SLOTTIME_ADJUST);
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_SLOTTIME, slot_time);
|
|
}
|
|
|
|
int
|
|
bwi_mac_attach(struct bwi_softc *sc, int id, uint8_t rev)
|
|
{
|
|
struct bwi_mac *mac;
|
|
int i;
|
|
|
|
KASSERT(sc->sc_nmac <= BWI_MAC_MAX && sc->sc_nmac >= 0,
|
|
("sc_nmac %d", sc->sc_nmac));
|
|
|
|
if (sc->sc_nmac == BWI_MAC_MAX) {
|
|
device_printf(sc->sc_dev, "too many MACs\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* More than one MAC is only supported by BCM4309
|
|
*/
|
|
if (sc->sc_nmac != 0 &&
|
|
sc->sc_pci_did != PCI_PRODUCT_BROADCOM_BCM4309) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
|
|
"ignore second MAC");
|
|
return 0;
|
|
}
|
|
|
|
mac = &sc->sc_mac[sc->sc_nmac];
|
|
|
|
/* XXX will this happen? */
|
|
if (BWI_REGWIN_EXIST(&mac->mac_regwin)) {
|
|
device_printf(sc->sc_dev, "%dth MAC already attached\n",
|
|
sc->sc_nmac);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Test whether the revision of this MAC is supported
|
|
*/
|
|
#define N(arr) (int)(sizeof(arr) / sizeof(arr[0]))
|
|
for (i = 0; i < N(bwi_sup_macrev); ++i) {
|
|
if (bwi_sup_macrev[i] == rev)
|
|
break;
|
|
}
|
|
if (i == N(bwi_sup_macrev)) {
|
|
device_printf(sc->sc_dev, "MAC rev %u is "
|
|
"not supported\n", rev);
|
|
return ENXIO;
|
|
}
|
|
#undef N
|
|
|
|
BWI_CREATE_MAC(mac, sc, id, rev);
|
|
sc->sc_nmac++;
|
|
|
|
if (mac->mac_rev < 5) {
|
|
mac->mac_flags |= BWI_MAC_F_HAS_TXSTATS;
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
|
|
"has TX stats");
|
|
} else {
|
|
mac->mac_flags |= BWI_MAC_F_PHYE_RESET;
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "MAC: rev %u\n", rev);
|
|
return 0;
|
|
}
|
|
|
|
static __inline void
|
|
bwi_mac_balance_atten(int *bbp_atten0, int *rf_atten0)
|
|
{
|
|
int bbp_atten, rf_atten, rf_atten_lim = -1;
|
|
|
|
bbp_atten = *bbp_atten0;
|
|
rf_atten = *rf_atten0;
|
|
|
|
/*
|
|
* RF attenuation affects TX power BWI_RF_ATTEN_FACTOR times
|
|
* as much as BBP attenuation, so we try our best to keep RF
|
|
* attenuation within range. BBP attenuation will be clamped
|
|
* later if it is out of range during balancing.
|
|
*
|
|
* BWI_RF_ATTEN_MAX0 is used as RF attenuation upper limit.
|
|
*/
|
|
|
|
/*
|
|
* Use BBP attenuation to balance RF attenuation
|
|
*/
|
|
if (rf_atten < 0)
|
|
rf_atten_lim = 0;
|
|
else if (rf_atten > BWI_RF_ATTEN_MAX0)
|
|
rf_atten_lim = BWI_RF_ATTEN_MAX0;
|
|
|
|
if (rf_atten_lim >= 0) {
|
|
bbp_atten += (BWI_RF_ATTEN_FACTOR * (rf_atten - rf_atten_lim));
|
|
rf_atten = rf_atten_lim;
|
|
}
|
|
|
|
/*
|
|
* If possible, use RF attenuation to balance BBP attenuation
|
|
* NOTE: RF attenuation is still kept within range.
|
|
*/
|
|
while (rf_atten < BWI_RF_ATTEN_MAX0 && bbp_atten > BWI_BBP_ATTEN_MAX) {
|
|
bbp_atten -= BWI_RF_ATTEN_FACTOR;
|
|
++rf_atten;
|
|
}
|
|
while (rf_atten > 0 && bbp_atten < 0) {
|
|
bbp_atten += BWI_RF_ATTEN_FACTOR;
|
|
--rf_atten;
|
|
}
|
|
|
|
/* RF attenuation MUST be within range */
|
|
KASSERT(rf_atten >= 0 && rf_atten <= BWI_RF_ATTEN_MAX0,
|
|
("rf_atten %d", rf_atten));
|
|
|
|
/*
|
|
* Clamp BBP attenuation
|
|
*/
|
|
if (bbp_atten < 0)
|
|
bbp_atten = 0;
|
|
else if (bbp_atten > BWI_BBP_ATTEN_MAX)
|
|
bbp_atten = BWI_BBP_ATTEN_MAX;
|
|
|
|
*rf_atten0 = rf_atten;
|
|
*bbp_atten0 = bbp_atten;
|
|
}
|
|
|
|
static void
|
|
bwi_mac_adjust_tpctl(struct bwi_mac *mac, int rf_atten_adj, int bbp_atten_adj)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
struct bwi_tpctl tpctl;
|
|
int bbp_atten, rf_atten, tp_ctrl1;
|
|
|
|
bcopy(&mac->mac_tpctl, &tpctl, sizeof(tpctl));
|
|
|
|
/* NOTE: Use signed value to do calulation */
|
|
bbp_atten = tpctl.bbp_atten;
|
|
rf_atten = tpctl.rf_atten;
|
|
tp_ctrl1 = tpctl.tp_ctrl1;
|
|
|
|
bbp_atten += bbp_atten_adj;
|
|
rf_atten += rf_atten_adj;
|
|
|
|
bwi_mac_balance_atten(&bbp_atten, &rf_atten);
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev == 2) {
|
|
if (rf_atten <= 1) {
|
|
if (tp_ctrl1 == 0) {
|
|
tp_ctrl1 = 3;
|
|
bbp_atten += 2;
|
|
rf_atten += 2;
|
|
} else if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) {
|
|
bbp_atten +=
|
|
(BWI_RF_ATTEN_FACTOR * (rf_atten - 2));
|
|
rf_atten = 2;
|
|
}
|
|
} else if (rf_atten > 4 && tp_ctrl1 != 0) {
|
|
tp_ctrl1 = 0;
|
|
if (bbp_atten < 3) {
|
|
bbp_atten += 2;
|
|
rf_atten -= 3;
|
|
} else {
|
|
bbp_atten -= 2;
|
|
rf_atten -= 2;
|
|
}
|
|
}
|
|
bwi_mac_balance_atten(&bbp_atten, &rf_atten);
|
|
}
|
|
|
|
tpctl.bbp_atten = bbp_atten;
|
|
tpctl.rf_atten = rf_atten;
|
|
tpctl.tp_ctrl1 = tp_ctrl1;
|
|
|
|
bwi_mac_lock(mac);
|
|
bwi_mac_set_tpctl_11bg(mac, &tpctl);
|
|
bwi_mac_unlock(mac);
|
|
}
|
|
|
|
/*
|
|
* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower
|
|
*/
|
|
void
|
|
bwi_mac_calibrate_txpower(struct bwi_mac *mac, enum bwi_txpwrcb_type type)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
int8_t tssi[4], tssi_avg, cur_txpwr;
|
|
int error, i, ofdm_tssi;
|
|
int txpwr_diff, rf_atten_adj, bbp_atten_adj;
|
|
|
|
if (!sc->sc_txpwr_calib)
|
|
return;
|
|
|
|
if (mac->mac_flags & BWI_MAC_F_TPCTL_ERROR) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
|
|
"tpctl error happened, can't set txpower");
|
|
return;
|
|
}
|
|
|
|
if (BWI_IS_BRCM_BU4306(sc)) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
|
|
"BU4306, can't set txpower");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Save latest TSSI and reset the related memory objects
|
|
*/
|
|
ofdm_tssi = 0;
|
|
error = bwi_rf_get_latest_tssi(mac, tssi, BWI_COMM_MOBJ_TSSI_DS);
|
|
if (error) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
|
|
"no DS tssi");
|
|
|
|
if (mac->mac_phy.phy_mode == IEEE80211_MODE_11B) {
|
|
if (type == BWI_TXPWR_FORCE) {
|
|
rf_atten_adj = 0;
|
|
bbp_atten_adj = 1;
|
|
goto calib;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
error = bwi_rf_get_latest_tssi(mac, tssi,
|
|
BWI_COMM_MOBJ_TSSI_OFDM);
|
|
if (error) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
|
|
"no OFDM tssi");
|
|
if (type == BWI_TXPWR_FORCE) {
|
|
rf_atten_adj = 0;
|
|
bbp_atten_adj = 1;
|
|
goto calib;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
tssi[i] += 0x20;
|
|
tssi[i] &= 0x3f;
|
|
}
|
|
ofdm_tssi = 1;
|
|
}
|
|
bwi_rf_clear_tssi(mac);
|
|
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER,
|
|
"tssi0 %d, tssi1 %d, tssi2 %d, tssi3 %d\n",
|
|
tssi[0], tssi[1], tssi[2], tssi[3]);
|
|
|
|
/*
|
|
* Calculate RF/BBP attenuation adjustment based on
|
|
* the difference between desired TX power and sampled
|
|
* TX power.
|
|
*/
|
|
/* +8 == "each incremented by 1/2" */
|
|
tssi_avg = (tssi[0] + tssi[1] + tssi[2] + tssi[3] + 8) / 4;
|
|
if (ofdm_tssi && (HFLAGS_READ(mac) & BWI_HFLAG_PWR_BOOST_DS))
|
|
tssi_avg -= 13;
|
|
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "tssi avg %d\n", tssi_avg);
|
|
|
|
error = bwi_rf_tssi2dbm(mac, tssi_avg, &cur_txpwr);
|
|
if (error)
|
|
return;
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "current txpower %d\n",
|
|
cur_txpwr);
|
|
|
|
txpwr_diff = rf->rf_txpower_max - cur_txpwr; /* XXX ni_txpower */
|
|
|
|
rf_atten_adj = -howmany(txpwr_diff, 8);
|
|
if (type == BWI_TXPWR_INIT) {
|
|
/*
|
|
* Move toward EEPROM max TX power as fast as we can
|
|
*/
|
|
bbp_atten_adj = -txpwr_diff;
|
|
} else {
|
|
bbp_atten_adj = -(txpwr_diff / 2);
|
|
}
|
|
bbp_atten_adj -= (BWI_RF_ATTEN_FACTOR * rf_atten_adj);
|
|
|
|
if (rf_atten_adj == 0 && bbp_atten_adj == 0) {
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
|
|
"no need to adjust RF/BBP attenuation");
|
|
/* TODO: LO */
|
|
return;
|
|
}
|
|
|
|
calib:
|
|
DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER,
|
|
"rf atten adjust %d, bbp atten adjust %d\n",
|
|
rf_atten_adj, bbp_atten_adj);
|
|
bwi_mac_adjust_tpctl(mac, rf_atten_adj, bbp_atten_adj);
|
|
/* TODO: LO */
|
|
}
|
|
|
|
static void
|
|
bwi_mac_lock(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
KASSERT((mac->mac_flags & BWI_MAC_F_LOCKED) == 0,
|
|
("mac_flags 0x%x", mac->mac_flags));
|
|
|
|
if (mac->mac_rev < 3)
|
|
bwi_mac_stop(mac);
|
|
else if (ic->ic_opmode != IEEE80211_M_HOSTAP)
|
|
bwi_mac_config_ps(mac);
|
|
|
|
CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_RFLOCK);
|
|
|
|
/* Flush pending bus write */
|
|
CSR_READ_4(sc, BWI_MAC_STATUS);
|
|
DELAY(10);
|
|
|
|
mac->mac_flags |= BWI_MAC_F_LOCKED;
|
|
}
|
|
|
|
static void
|
|
bwi_mac_unlock(struct bwi_mac *mac)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
KASSERT(mac->mac_flags & BWI_MAC_F_LOCKED,
|
|
("mac_flags 0x%x", mac->mac_flags));
|
|
|
|
CSR_READ_2(sc, BWI_PHYINFO); /* dummy read */
|
|
|
|
CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_RFLOCK);
|
|
|
|
if (mac->mac_rev < 3)
|
|
bwi_mac_start(mac);
|
|
else if (ic->ic_opmode != IEEE80211_M_HOSTAP)
|
|
bwi_mac_config_ps(mac);
|
|
|
|
mac->mac_flags &= ~BWI_MAC_F_LOCKED;
|
|
}
|
|
|
|
void
|
|
bwi_mac_set_promisc(struct bwi_mac *mac, int promisc)
|
|
{
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
if (mac->mac_rev < 5) /* Promisc is always on */
|
|
return;
|
|
|
|
if (promisc)
|
|
CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PROMISC);
|
|
else
|
|
CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PROMISC);
|
|
}
|