Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
/*
|
|
|
|
* 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/bwirf.c,v 1.9 2008/08/21 12:19:33 swildner Exp $
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_bwi.h"
|
2012-05-12 15:11:53 +00:00
|
|
|
#include "opt_wlan.h"
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
|
|
|
|
#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 <net/if.h>
|
2013-10-26 17:58:36 +00:00
|
|
|
#include <net/if_var.h>
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
#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 <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>
|
|
|
|
|
|
|
|
#define RF_LO_WRITE(mac, lo) bwi_rf_lo_write((mac), (lo))
|
|
|
|
|
|
|
|
#define BWI_RF_2GHZ_CHAN(chan) \
|
|
|
|
(ieee80211_ieee2mhz((chan), IEEE80211_CHAN_2GHZ) - 2400)
|
|
|
|
|
|
|
|
#define BWI_DEFAULT_IDLE_TSSI 52
|
|
|
|
|
|
|
|
struct rf_saveregs {
|
|
|
|
uint16_t phy_01;
|
|
|
|
uint16_t phy_03;
|
|
|
|
uint16_t phy_0a;
|
|
|
|
uint16_t phy_15;
|
|
|
|
uint16_t phy_2a;
|
|
|
|
uint16_t phy_30;
|
|
|
|
uint16_t phy_35;
|
|
|
|
uint16_t phy_60;
|
|
|
|
uint16_t phy_429;
|
|
|
|
uint16_t phy_802;
|
|
|
|
uint16_t phy_811;
|
|
|
|
uint16_t phy_812;
|
|
|
|
uint16_t phy_814;
|
|
|
|
uint16_t phy_815;
|
|
|
|
|
|
|
|
uint16_t rf_43;
|
|
|
|
uint16_t rf_52;
|
|
|
|
uint16_t rf_7a;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SAVE_RF_REG(mac, regs, n) (regs)->rf_##n = RF_READ((mac), 0x##n)
|
|
|
|
#define RESTORE_RF_REG(mac, regs, n) RF_WRITE((mac), 0x##n, (regs)->rf_##n)
|
|
|
|
|
|
|
|
#define SAVE_PHY_REG(mac, regs, n) (regs)->phy_##n = PHY_READ((mac), 0x##n)
|
|
|
|
#define RESTORE_PHY_REG(mac, regs, n) PHY_WRITE((mac), 0x##n, (regs)->phy_##n)
|
|
|
|
|
|
|
|
static int bwi_rf_calc_txpower(int8_t *, uint8_t, const int16_t[]);
|
|
|
|
static void bwi_rf_work_around(struct bwi_mac *, u_int);
|
|
|
|
static int bwi_rf_gain_max_reached(struct bwi_mac *, int);
|
|
|
|
static uint16_t bwi_rf_calibval(struct bwi_mac *);
|
|
|
|
static uint16_t bwi_rf_get_tp_ctrl2(struct bwi_mac *);
|
|
|
|
|
|
|
|
static void bwi_rf_lo_update_11b(struct bwi_mac *);
|
|
|
|
static uint16_t bwi_rf_lo_measure_11b(struct bwi_mac *);
|
|
|
|
|
|
|
|
static void bwi_rf_lo_update_11g(struct bwi_mac *);
|
|
|
|
static uint32_t bwi_rf_lo_devi_measure(struct bwi_mac *, uint16_t);
|
|
|
|
static void bwi_rf_lo_measure_11g(struct bwi_mac *,
|
|
|
|
const struct bwi_rf_lo *, struct bwi_rf_lo *, uint8_t);
|
|
|
|
static uint8_t _bwi_rf_lo_update_11g(struct bwi_mac *, uint16_t);
|
|
|
|
static void bwi_rf_lo_write(struct bwi_mac *, const struct bwi_rf_lo *);
|
|
|
|
|
|
|
|
static void bwi_rf_set_nrssi_ofs_11g(struct bwi_mac *);
|
|
|
|
static void bwi_rf_calc_nrssi_slope_11b(struct bwi_mac *);
|
|
|
|
static void bwi_rf_calc_nrssi_slope_11g(struct bwi_mac *);
|
|
|
|
static void bwi_rf_set_nrssi_thr_11b(struct bwi_mac *);
|
|
|
|
static void bwi_rf_set_nrssi_thr_11g(struct bwi_mac *);
|
|
|
|
|
|
|
|
static void bwi_rf_init_sw_nrssi_table(struct bwi_mac *);
|
|
|
|
|
|
|
|
static int bwi_rf_calc_rssi_bcm2050(struct bwi_mac *,
|
|
|
|
const struct bwi_rxbuf_hdr *);
|
|
|
|
static int bwi_rf_calc_rssi_bcm2053(struct bwi_mac *,
|
|
|
|
const struct bwi_rxbuf_hdr *);
|
|
|
|
static int bwi_rf_calc_rssi_bcm2060(struct bwi_mac *,
|
|
|
|
const struct bwi_rxbuf_hdr *);
|
|
|
|
static int bwi_rf_calc_noise_bcm2050(struct bwi_mac *);
|
|
|
|
static int bwi_rf_calc_noise_bcm2053(struct bwi_mac *);
|
|
|
|
static int bwi_rf_calc_noise_bcm2060(struct bwi_mac *);
|
|
|
|
|
|
|
|
static void bwi_rf_on_11a(struct bwi_mac *);
|
|
|
|
static void bwi_rf_on_11bg(struct bwi_mac *);
|
|
|
|
|
|
|
|
static void bwi_rf_off_11a(struct bwi_mac *);
|
|
|
|
static void bwi_rf_off_11bg(struct bwi_mac *);
|
|
|
|
static void bwi_rf_off_11g_rev5(struct bwi_mac *);
|
|
|
|
|
|
|
|
static const int8_t bwi_txpower_map_11b[BWI_TSSI_MAX] =
|
|
|
|
{ BWI_TXPOWER_MAP_11B };
|
|
|
|
static const int8_t bwi_txpower_map_11g[BWI_TSSI_MAX] =
|
|
|
|
{ BWI_TXPOWER_MAP_11G };
|
|
|
|
|
|
|
|
static __inline int16_t
|
|
|
|
bwi_nrssi_11g(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
int16_t val;
|
|
|
|
|
|
|
|
#define NRSSI_11G_MASK __BITS(13, 8)
|
|
|
|
|
|
|
|
val = (int16_t)__SHIFTOUT(PHY_READ(mac, 0x47f), NRSSI_11G_MASK);
|
|
|
|
if (val >= 32)
|
|
|
|
val -= 64;
|
|
|
|
return val;
|
|
|
|
|
|
|
|
#undef NRSSI_11G_MASK
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline struct bwi_rf_lo *
|
|
|
|
bwi_get_rf_lo(struct bwi_mac *mac, uint16_t rf_atten, uint16_t bbp_atten)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
n = rf_atten + (14 * (bbp_atten / 2));
|
|
|
|
KASSERT(n < BWI_RFLO_MAX, ("n %d", n));
|
|
|
|
|
|
|
|
return &mac->mac_rf.rf_lo[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline int
|
|
|
|
bwi_rf_lo_isused(struct bwi_mac *mac, const struct bwi_rf_lo *lo)
|
|
|
|
{
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
idx = lo - rf->rf_lo;
|
|
|
|
KASSERT(idx >= 0 && idx < BWI_RFLO_MAX, ("idx %d", idx));
|
|
|
|
|
|
|
|
return isset(rf->rf_lo_used, idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_write(struct bwi_mac *mac, uint16_t ctrl, uint16_t data)
|
|
|
|
{
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CTRL, ctrl);
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_DATA_LO, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t
|
|
|
|
bwi_rf_read(struct bwi_mac *mac, uint16_t ctrl)
|
|
|
|
{
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
ctrl |= rf->rf_ctrl_rd;
|
|
|
|
if (rf->rf_ctrl_adj) {
|
|
|
|
/* XXX */
|
|
|
|
if (ctrl < 0x70)
|
|
|
|
ctrl += 0x80;
|
|
|
|
else if (ctrl < 0x80)
|
|
|
|
ctrl += 0x70;
|
|
|
|
}
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CTRL, ctrl);
|
|
|
|
return CSR_READ_2(sc, BWI_RF_DATA_LO);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bwi_rf_attach(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;
|
|
|
|
uint16_t type, manu;
|
|
|
|
uint8_t rev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get RF manufacture/type/revision
|
|
|
|
*/
|
|
|
|
if (sc->sc_bbp_id == BWI_BBPID_BCM4317) {
|
|
|
|
/*
|
|
|
|
* Fake a BCM2050 RF
|
|
|
|
*/
|
|
|
|
manu = BWI_RF_MANUFACT_BCM;
|
|
|
|
type = BWI_RF_T_BCM2050;
|
|
|
|
if (sc->sc_bbp_rev == 0)
|
|
|
|
rev = 3;
|
|
|
|
else if (sc->sc_bbp_rev == 1)
|
|
|
|
rev = 4;
|
|
|
|
else
|
|
|
|
rev = 5;
|
|
|
|
} else {
|
|
|
|
uint32_t val;
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CTRL, BWI_RF_CTRL_RFINFO);
|
|
|
|
val = CSR_READ_2(sc, BWI_RF_DATA_HI);
|
|
|
|
val <<= 16;
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CTRL, BWI_RF_CTRL_RFINFO);
|
|
|
|
val |= CSR_READ_2(sc, BWI_RF_DATA_LO);
|
|
|
|
|
|
|
|
manu = __SHIFTOUT(val, BWI_RFINFO_MANUFACT_MASK);
|
|
|
|
type = __SHIFTOUT(val, BWI_RFINFO_TYPE_MASK);
|
|
|
|
rev = __SHIFTOUT(val, BWI_RFINFO_REV_MASK);
|
|
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "RF: manu 0x%03x, type 0x%04x, rev %u\n",
|
|
|
|
manu, type, rev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify whether the RF is supported
|
|
|
|
*/
|
|
|
|
rf->rf_ctrl_rd = 0;
|
|
|
|
rf->rf_ctrl_adj = 0;
|
|
|
|
switch (phy->phy_mode) {
|
|
|
|
case IEEE80211_MODE_11A:
|
|
|
|
if (manu != BWI_RF_MANUFACT_BCM ||
|
|
|
|
type != BWI_RF_T_BCM2060 ||
|
|
|
|
rev != 1) {
|
|
|
|
device_printf(sc->sc_dev, "only BCM2060 rev 1 RF "
|
|
|
|
"is supported for 11A PHY\n");
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
rf->rf_ctrl_rd = BWI_RF_CTRL_RD_11A;
|
|
|
|
rf->rf_on = bwi_rf_on_11a;
|
|
|
|
rf->rf_off = bwi_rf_off_11a;
|
|
|
|
rf->rf_calc_rssi = bwi_rf_calc_rssi_bcm2060;
|
|
|
|
rf->rf_calc_noise = bwi_rf_calc_noise_bcm2060;
|
|
|
|
break;
|
|
|
|
case IEEE80211_MODE_11B:
|
|
|
|
if (type == BWI_RF_T_BCM2050) {
|
|
|
|
rf->rf_ctrl_rd = BWI_RF_CTRL_RD_11BG;
|
|
|
|
rf->rf_calc_rssi = bwi_rf_calc_rssi_bcm2050;
|
|
|
|
rf->rf_calc_noise = bwi_rf_calc_noise_bcm2050;
|
|
|
|
} else if (type == BWI_RF_T_BCM2053) {
|
|
|
|
rf->rf_ctrl_adj = 1;
|
|
|
|
rf->rf_calc_rssi = bwi_rf_calc_rssi_bcm2053;
|
|
|
|
rf->rf_calc_noise = bwi_rf_calc_noise_bcm2053;
|
|
|
|
} else {
|
|
|
|
device_printf(sc->sc_dev, "only BCM2050/BCM2053 RF "
|
|
|
|
"is supported for 11B PHY\n");
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
rf->rf_on = bwi_rf_on_11bg;
|
|
|
|
rf->rf_off = bwi_rf_off_11bg;
|
|
|
|
rf->rf_calc_nrssi_slope = bwi_rf_calc_nrssi_slope_11b;
|
|
|
|
rf->rf_set_nrssi_thr = bwi_rf_set_nrssi_thr_11b;
|
|
|
|
if (phy->phy_rev == 6)
|
|
|
|
rf->rf_lo_update = bwi_rf_lo_update_11g;
|
|
|
|
else
|
|
|
|
rf->rf_lo_update = bwi_rf_lo_update_11b;
|
|
|
|
break;
|
|
|
|
case IEEE80211_MODE_11G:
|
|
|
|
if (type != BWI_RF_T_BCM2050) {
|
|
|
|
device_printf(sc->sc_dev, "only BCM2050 RF "
|
|
|
|
"is supported for 11G PHY\n");
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
rf->rf_ctrl_rd = BWI_RF_CTRL_RD_11BG;
|
|
|
|
rf->rf_on = bwi_rf_on_11bg;
|
|
|
|
if (mac->mac_rev >= 5)
|
|
|
|
rf->rf_off = bwi_rf_off_11g_rev5;
|
|
|
|
else
|
|
|
|
rf->rf_off = bwi_rf_off_11bg;
|
|
|
|
rf->rf_calc_nrssi_slope = bwi_rf_calc_nrssi_slope_11g;
|
|
|
|
rf->rf_set_nrssi_thr = bwi_rf_set_nrssi_thr_11g;
|
|
|
|
rf->rf_calc_rssi = bwi_rf_calc_rssi_bcm2050;
|
|
|
|
rf->rf_calc_noise = bwi_rf_calc_noise_bcm2050;
|
|
|
|
rf->rf_lo_update = bwi_rf_lo_update_11g;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
device_printf(sc->sc_dev, "unsupported PHY mode\n");
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
rf->rf_type = type;
|
|
|
|
rf->rf_rev = rev;
|
|
|
|
rf->rf_manu = manu;
|
|
|
|
rf->rf_curchan = IEEE80211_CHAN_ANY;
|
|
|
|
rf->rf_ant_mode = BWI_ANT_MODE_AUTO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_set_chan(struct bwi_mac *mac, u_int chan, int work_around)
|
|
|
|
{
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
if (chan == IEEE80211_CHAN_ANY)
|
|
|
|
return;
|
|
|
|
|
|
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_CHAN, chan);
|
|
|
|
|
|
|
|
/* TODO: 11A */
|
|
|
|
|
|
|
|
if (work_around)
|
|
|
|
bwi_rf_work_around(mac, chan);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN, BWI_RF_2GHZ_CHAN(chan));
|
|
|
|
|
|
|
|
if (chan == 14) {
|
|
|
|
if (sc->sc_locale == BWI_SPROM_LOCALE_JAPAN)
|
|
|
|
HFLAGS_CLRBITS(mac, BWI_HFLAG_NOT_JAPAN);
|
|
|
|
else
|
|
|
|
HFLAGS_SETBITS(mac, BWI_HFLAG_NOT_JAPAN);
|
|
|
|
CSR_SETBITS_2(sc, BWI_RF_CHAN_EX, (1 << 11)); /* XXX */
|
|
|
|
} else {
|
|
|
|
CSR_CLRBITS_2(sc, BWI_RF_CHAN_EX, 0x840); /* XXX */
|
|
|
|
}
|
|
|
|
DELAY(8000); /* DELAY(2000); */
|
|
|
|
|
|
|
|
mac->mac_rf.rf_curchan = chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_get_gains(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
#define SAVE_PHY_MAX 15
|
|
|
|
#define SAVE_RF_MAX 3
|
|
|
|
|
|
|
|
static const uint16_t save_rf_regs[SAVE_RF_MAX] =
|
|
|
|
{ 0x52, 0x43, 0x7a };
|
|
|
|
static const uint16_t save_phy_regs[SAVE_PHY_MAX] = {
|
|
|
|
0x0429, 0x0001, 0x0811, 0x0812,
|
|
|
|
0x0814, 0x0815, 0x005a, 0x0059,
|
|
|
|
0x0058, 0x000a, 0x0003, 0x080f,
|
|
|
|
0x0810, 0x002b, 0x0015
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
uint16_t save_phy[SAVE_PHY_MAX];
|
|
|
|
uint16_t save_rf[SAVE_RF_MAX];
|
|
|
|
uint16_t trsw;
|
|
|
|
int i, j, loop1_max, loop1, loop2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save PHY/RF registers for later restoration
|
|
|
|
*/
|
|
|
|
for (i = 0; i < SAVE_PHY_MAX; ++i)
|
|
|
|
save_phy[i] = PHY_READ(mac, save_phy_regs[i]);
|
|
|
|
PHY_READ(mac, 0x2d); /* dummy read */
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
save_rf[i] = RF_READ(mac, save_rf_regs[i]);
|
|
|
|
|
|
|
|
PHY_CLRBITS(mac, 0x429, 0xc000);
|
|
|
|
PHY_SETBITS(mac, 0x1, 0x8000);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x2);
|
|
|
|
PHY_CLRBITS(mac, 0x812, 0x2);
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x1);
|
|
|
|
PHY_CLRBITS(mac, 0x812, 0x1);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x814, 0x1);
|
|
|
|
PHY_CLRBITS(mac, 0x815, 0x1);
|
|
|
|
PHY_SETBITS(mac, 0x814, 0x2);
|
|
|
|
PHY_CLRBITS(mac, 0x815, 0x2);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x811, 0xc);
|
|
|
|
PHY_SETBITS(mac, 0x812, 0xc);
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x30);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x812, 0xffcf, 0x10);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x5a, 0x780);
|
|
|
|
PHY_WRITE(mac, 0x59, 0xc810);
|
|
|
|
PHY_WRITE(mac, 0x58, 0xd);
|
|
|
|
PHY_SETBITS(mac, 0xa, 0x2000);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x814, 0x4);
|
|
|
|
PHY_CLRBITS(mac, 0x815, 0x4);
|
|
|
|
|
|
|
|
PHY_FILT_SETBITS(mac, 0x3, 0xff9f, 0x40);
|
|
|
|
|
|
|
|
if (rf->rf_rev == 8) {
|
|
|
|
loop1_max = 15;
|
|
|
|
RF_WRITE(mac, 0x43, loop1_max);
|
|
|
|
} else {
|
|
|
|
loop1_max = 9;
|
|
|
|
RF_WRITE(mac, 0x52, 0x0);
|
|
|
|
RF_FILT_SETBITS(mac, 0x43, 0xfff0, loop1_max);
|
|
|
|
}
|
|
|
|
|
|
|
|
bwi_phy_set_bbp_atten(mac, 11);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 3)
|
|
|
|
PHY_WRITE(mac, 0x80f, 0xc020);
|
|
|
|
else
|
|
|
|
PHY_WRITE(mac, 0x80f, 0x8020);
|
|
|
|
PHY_WRITE(mac, 0x810, 0);
|
|
|
|
|
|
|
|
PHY_FILT_SETBITS(mac, 0x2b, 0xffc0, 0x1);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x2b, 0xc0ff, 0x800);
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x100);
|
|
|
|
PHY_CLRBITS(mac, 0x812, 0x3000);
|
|
|
|
|
|
|
|
if ((sc->sc_card_flags & BWI_CARD_F_EXT_LNA) &&
|
|
|
|
phy->phy_rev >= 7) {
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x800);
|
|
|
|
PHY_SETBITS(mac, 0x812, 0x8000);
|
|
|
|
}
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xff08);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out 'loop1/loop2', which will be used to calculate
|
|
|
|
* max loopback gain later
|
|
|
|
*/
|
|
|
|
j = 0;
|
|
|
|
for (i = 0; i < loop1_max; ++i) {
|
|
|
|
for (j = 0; j < 16; ++j) {
|
|
|
|
RF_WRITE(mac, 0x43, i);
|
|
|
|
|
|
|
|
if (bwi_rf_gain_max_reached(mac, j))
|
|
|
|
goto loop1_exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loop1_exit:
|
|
|
|
loop1 = i;
|
|
|
|
loop2 = j;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out 'trsw', which will be used to calculate
|
|
|
|
* TRSW(TX/RX switch) RX gain later
|
|
|
|
*/
|
|
|
|
if (loop2 >= 8) {
|
|
|
|
PHY_SETBITS(mac, 0x812, 0x30);
|
|
|
|
trsw = 0x1b;
|
|
|
|
for (i = loop2 - 8; i < 16; ++i) {
|
|
|
|
trsw -= 3;
|
|
|
|
if (bwi_rf_gain_max_reached(mac, i))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
trsw = 0x18;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore saved PHY/RF registers
|
|
|
|
*/
|
|
|
|
/* First 4 saved PHY registers need special processing */
|
|
|
|
for (i = 4; i < SAVE_PHY_MAX; ++i)
|
|
|
|
PHY_WRITE(mac, save_phy_regs[i], save_phy[i]);
|
|
|
|
|
|
|
|
bwi_phy_set_bbp_atten(mac, mac->mac_tpctl.bbp_atten);
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
RF_WRITE(mac, save_rf_regs[i], save_rf[i]);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, save_phy_regs[2], save_phy[2] | 0x3);
|
|
|
|
DELAY(10);
|
|
|
|
PHY_WRITE(mac, save_phy_regs[2], save_phy[2]);
|
|
|
|
PHY_WRITE(mac, save_phy_regs[3], save_phy[3]);
|
|
|
|
PHY_WRITE(mac, save_phy_regs[0], save_phy[0]);
|
|
|
|
PHY_WRITE(mac, save_phy_regs[1], save_phy[1]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate gains
|
|
|
|
*/
|
|
|
|
rf->rf_lo_gain = (loop2 * 6) - (loop1 * 4) - 11;
|
|
|
|
rf->rf_rx_gain = trsw * 2;
|
|
|
|
DPRINTF(mac->mac_sc, BWI_DBG_RF | BWI_DBG_INIT,
|
|
|
|
"lo gain: %u, rx gain: %u\n",
|
|
|
|
rf->rf_lo_gain, rf->rf_rx_gain);
|
|
|
|
|
|
|
|
#undef SAVE_RF_MAX
|
|
|
|
#undef SAVE_PHY_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_init(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2060) {
|
|
|
|
/* TODO: 11A */
|
|
|
|
} else {
|
|
|
|
if (rf->rf_flags & BWI_RF_F_INITED)
|
|
|
|
RF_WRITE(mac, 0x78, rf->rf_calib);
|
|
|
|
else
|
|
|
|
bwi_rf_init_bcm2050(mac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_off_11a(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
RF_WRITE(mac, 0x4, 0xff);
|
|
|
|
RF_WRITE(mac, 0x5, 0xfb);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x10, 0x8);
|
|
|
|
PHY_SETBITS(mac, 0x11, 0x8);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x15, 0xaa00);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_off_11bg(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
PHY_WRITE(mac, 0x15, 0xaa00);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_off_11g_rev5(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x8c);
|
|
|
|
PHY_CLRBITS(mac, 0x812, 0x8c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_work_around(struct bwi_mac *mac, u_int chan)
|
|
|
|
{
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
|
|
|
|
if (chan == IEEE80211_CHAN_ANY) {
|
|
|
|
device_printf(sc->sc_dev, "%s invalid channel!!\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rf->rf_type != BWI_RF_T_BCM2050 || rf->rf_rev >= 6)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (chan <= 10)
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN, BWI_RF_2GHZ_CHAN(chan + 4));
|
|
|
|
else
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN, BWI_RF_2GHZ_CHAN(1));
|
|
|
|
DELAY(1000);
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN, BWI_RF_2GHZ_CHAN(chan));
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline struct bwi_rf_lo *
|
|
|
|
bwi_rf_lo_find(struct bwi_mac *mac, const struct bwi_tpctl *tpctl)
|
|
|
|
{
|
|
|
|
uint16_t rf_atten, bbp_atten;
|
|
|
|
int remap_rf_atten;
|
|
|
|
|
|
|
|
remap_rf_atten = 1;
|
|
|
|
if (tpctl == NULL) {
|
|
|
|
bbp_atten = 2;
|
|
|
|
rf_atten = 3;
|
|
|
|
} else {
|
|
|
|
if (tpctl->tp_ctrl1 == 3)
|
|
|
|
remap_rf_atten = 0;
|
|
|
|
|
|
|
|
bbp_atten = tpctl->bbp_atten;
|
|
|
|
rf_atten = tpctl->rf_atten;
|
|
|
|
|
|
|
|
if (bbp_atten > 6)
|
|
|
|
bbp_atten = 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remap_rf_atten) {
|
|
|
|
#define MAP_MAX 10
|
|
|
|
static const uint16_t map[MAP_MAX] =
|
|
|
|
{ 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 };
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
KASSERT(rf_atten < MAP_MAX, ("rf_atten %d", rf_atten));
|
|
|
|
rf_atten = map[rf_atten];
|
|
|
|
#else
|
|
|
|
if (rf_atten >= MAP_MAX) {
|
|
|
|
rf_atten = 0; /* XXX */
|
|
|
|
} else {
|
|
|
|
rf_atten = map[rf_atten];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#undef MAP_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
return bwi_get_rf_lo(mac, rf_atten, bbp_atten);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_lo_adjust(struct bwi_mac *mac, const struct bwi_tpctl *tpctl)
|
|
|
|
{
|
|
|
|
const struct bwi_rf_lo *lo;
|
|
|
|
|
|
|
|
lo = bwi_rf_lo_find(mac, tpctl);
|
|
|
|
RF_LO_WRITE(mac, lo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_lo_write(struct bwi_mac *mac, const struct bwi_rf_lo *lo)
|
|
|
|
{
|
|
|
|
uint16_t val;
|
|
|
|
|
|
|
|
val = (uint8_t)lo->ctrl_lo;
|
|
|
|
val |= ((uint8_t)lo->ctrl_hi) << 8;
|
|
|
|
|
|
|
|
PHY_WRITE(mac, BWI_PHYR_RF_LO, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_gain_max_reached(struct bwi_mac *mac, int idx)
|
|
|
|
{
|
|
|
|
PHY_FILT_SETBITS(mac, 0x812, 0xf0ff, idx << 8);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x15, 0xfff, 0xa000);
|
|
|
|
PHY_SETBITS(mac, 0x15, 0xf000);
|
|
|
|
|
|
|
|
DELAY(20);
|
|
|
|
|
|
|
|
return (PHY_READ(mac, 0x2d) >= 0xdfc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX use bitmap array */
|
|
|
|
static __inline uint16_t
|
|
|
|
bitswap4(uint16_t val)
|
|
|
|
{
|
|
|
|
uint16_t ret;
|
|
|
|
|
|
|
|
ret = (val & 0x8) >> 3;
|
|
|
|
ret |= (val & 0x4) >> 1;
|
|
|
|
ret |= (val & 0x2) << 1;
|
|
|
|
ret |= (val & 0x1) << 3;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline uint16_t
|
|
|
|
bwi_phy812_value(struct bwi_mac *mac, uint16_t lpd)
|
|
|
|
{
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
uint16_t lo_gain, ext_lna, loop;
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lo_gain = rf->rf_lo_gain;
|
|
|
|
if (rf->rf_rev == 8)
|
|
|
|
lo_gain += 0x3e;
|
|
|
|
else
|
|
|
|
lo_gain += 0x26;
|
|
|
|
|
|
|
|
if (lo_gain >= 0x46) {
|
|
|
|
lo_gain -= 0x46;
|
|
|
|
ext_lna = 0x3000;
|
|
|
|
} else if (lo_gain >= 0x3a) {
|
|
|
|
lo_gain -= 0x3a;
|
|
|
|
ext_lna = 0x1000;
|
|
|
|
} else if (lo_gain >= 0x2e) {
|
|
|
|
lo_gain -= 0x2e;
|
|
|
|
ext_lna = 0x2000;
|
|
|
|
} else {
|
|
|
|
lo_gain -= 0x10;
|
|
|
|
ext_lna = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (loop = 0; loop < 16; ++loop) {
|
|
|
|
lo_gain -= (6 * loop);
|
|
|
|
if (lo_gain < 6)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 7 && (sc->sc_card_flags & BWI_CARD_F_EXT_LNA)) {
|
|
|
|
if (ext_lna)
|
|
|
|
ext_lna |= 0x8000;
|
|
|
|
ext_lna |= (loop << 8);
|
|
|
|
switch (lpd) {
|
|
|
|
case 0x011:
|
|
|
|
return 0x8f92;
|
|
|
|
case 0x001:
|
|
|
|
return (0x8092 | ext_lna);
|
|
|
|
case 0x101:
|
|
|
|
return (0x2092 | ext_lna);
|
|
|
|
case 0x100:
|
|
|
|
return (0x2093 | ext_lna);
|
|
|
|
default:
|
|
|
|
panic("unsupported lpd\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ext_lna |= (loop << 8);
|
|
|
|
switch (lpd) {
|
|
|
|
case 0x011:
|
|
|
|
return 0xf92;
|
|
|
|
case 0x001:
|
|
|
|
case 0x101:
|
|
|
|
return (0x92 | ext_lna);
|
|
|
|
case 0x100:
|
|
|
|
return (0x93 | ext_lna);
|
|
|
|
default:
|
|
|
|
panic("unsupported lpd\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("never reached\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_init_bcm2050(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
#define SAVE_RF_MAX 3
|
|
|
|
#define SAVE_PHY_COMM_MAX 4
|
|
|
|
#define SAVE_PHY_11G_MAX 6
|
|
|
|
|
|
|
|
static const uint16_t save_rf_regs[SAVE_RF_MAX] =
|
|
|
|
{ 0x0043, 0x0051, 0x0052 };
|
|
|
|
static const uint16_t save_phy_regs_comm[SAVE_PHY_COMM_MAX] =
|
|
|
|
{ 0x0015, 0x005a, 0x0059, 0x0058 };
|
|
|
|
static const uint16_t save_phy_regs_11g[SAVE_PHY_11G_MAX] =
|
|
|
|
{ 0x0811, 0x0812, 0x0814, 0x0815, 0x0429, 0x0802 };
|
|
|
|
|
|
|
|
uint16_t save_rf[SAVE_RF_MAX];
|
|
|
|
uint16_t save_phy_comm[SAVE_PHY_COMM_MAX];
|
|
|
|
uint16_t save_phy_11g[SAVE_PHY_11G_MAX];
|
|
|
|
uint16_t phyr_35, phyr_30 = 0, rfr_78, phyr_80f = 0, phyr_810 = 0;
|
|
|
|
uint16_t bphy_ctrl = 0, bbp_atten, rf_chan_ex;
|
|
|
|
uint16_t phy812_val;
|
|
|
|
uint16_t calib;
|
|
|
|
uint32_t test_lim, test;
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save registers for later restoring
|
|
|
|
*/
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
save_rf[i] = RF_READ(mac, save_rf_regs[i]);
|
|
|
|
for (i = 0; i < SAVE_PHY_COMM_MAX; ++i)
|
|
|
|
save_phy_comm[i] = PHY_READ(mac, save_phy_regs_comm[i]);
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11B) {
|
|
|
|
phyr_30 = PHY_READ(mac, 0x30);
|
|
|
|
bphy_ctrl = CSR_READ_2(sc, BWI_BPHY_CTRL);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x30, 0xff);
|
|
|
|
CSR_WRITE_2(sc, BWI_BPHY_CTRL, 0x3f3f);
|
|
|
|
} else if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
for (i = 0; i < SAVE_PHY_11G_MAX; ++i) {
|
|
|
|
save_phy_11g[i] =
|
|
|
|
PHY_READ(mac, save_phy_regs_11g[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x814, 0x3);
|
|
|
|
PHY_CLRBITS(mac, 0x815, 0x3);
|
|
|
|
PHY_CLRBITS(mac, 0x429, 0x8000);
|
|
|
|
PHY_CLRBITS(mac, 0x802, 0x3);
|
|
|
|
|
|
|
|
phyr_80f = PHY_READ(mac, 0x80f);
|
|
|
|
phyr_810 = PHY_READ(mac, 0x810);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 3)
|
|
|
|
PHY_WRITE(mac, 0x80f, 0xc020);
|
|
|
|
else
|
|
|
|
PHY_WRITE(mac, 0x80f, 0x8020);
|
|
|
|
PHY_WRITE(mac, 0x810, 0);
|
|
|
|
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x011);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
if (phy->phy_rev < 7 ||
|
|
|
|
(sc->sc_card_flags & BWI_CARD_F_EXT_LNA) == 0)
|
|
|
|
PHY_WRITE(mac, 0x811, 0x1b3);
|
|
|
|
else
|
|
|
|
PHY_WRITE(mac, 0x811, 0x9b3);
|
|
|
|
}
|
|
|
|
CSR_SETBITS_2(sc, BWI_RF_ANTDIV, 0x8000);
|
|
|
|
|
|
|
|
phyr_35 = PHY_READ(mac, 0x35);
|
|
|
|
PHY_CLRBITS(mac, 0x35, 0x80);
|
|
|
|
|
|
|
|
bbp_atten = CSR_READ_2(sc, BWI_BBP_ATTEN);
|
|
|
|
rf_chan_ex = CSR_READ_2(sc, BWI_RF_CHAN_EX);
|
|
|
|
|
|
|
|
if (phy->phy_version == 0) {
|
|
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0x122);
|
|
|
|
} else {
|
|
|
|
if (phy->phy_version >= 2)
|
|
|
|
PHY_FILT_SETBITS(mac, 0x3, 0xffbf, 0x40);
|
|
|
|
CSR_SETBITS_2(sc, BWI_RF_CHAN_EX, 0x2000);
|
|
|
|
}
|
|
|
|
|
|
|
|
calib = bwi_rf_calibval(mac);
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11B)
|
|
|
|
RF_WRITE(mac, 0x78, 0x26);
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x011);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x15, 0xbfaf);
|
|
|
|
PHY_WRITE(mac, 0x2b, 0x1403);
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x001);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x15, 0xbfa0);
|
|
|
|
|
|
|
|
RF_SETBITS(mac, 0x51, 0x4);
|
|
|
|
if (rf->rf_rev == 8) {
|
|
|
|
RF_WRITE(mac, 0x43, 0x1f);
|
|
|
|
} else {
|
|
|
|
RF_WRITE(mac, 0x52, 0);
|
|
|
|
RF_FILT_SETBITS(mac, 0x43, 0xfff0, 0x9);
|
|
|
|
}
|
|
|
|
|
|
|
|
test_lim = 0;
|
|
|
|
PHY_WRITE(mac, 0x58, 0);
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
|
|
PHY_WRITE(mac, 0x5a, 0x480);
|
|
|
|
PHY_WRITE(mac, 0x59, 0xc810);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x58, 0xd);
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x101);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xafb0);
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x101);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xefb0);
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x100);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xfff0);
|
|
|
|
DELAY(20);
|
|
|
|
|
|
|
|
test_lim += PHY_READ(mac, 0x2d);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x58, 0);
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x101);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xafb0);
|
|
|
|
}
|
|
|
|
++test_lim;
|
|
|
|
test_lim >>= 9;
|
|
|
|
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
test = 0;
|
|
|
|
PHY_WRITE(mac, 0x58, 0);
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
|
|
int j;
|
|
|
|
|
|
|
|
rfr_78 = (bitswap4(i) << 1) | 0x20;
|
|
|
|
RF_WRITE(mac, 0x78, rfr_78);
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
/* NB: This block is slight different than the above one */
|
|
|
|
for (j = 0; j < 16; ++j) {
|
|
|
|
PHY_WRITE(mac, 0x5a, 0xd80);
|
|
|
|
PHY_WRITE(mac, 0x59, 0xc810);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x58, 0xd);
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) ||
|
|
|
|
phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x101);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xafb0);
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) ||
|
|
|
|
phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x101);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xefb0);
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) ||
|
|
|
|
phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x100);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xfff0);
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
test += PHY_READ(mac, 0x2d);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x58, 0);
|
|
|
|
if ((phy->phy_flags & BWI_PHY_F_LINKED) ||
|
|
|
|
phy->phy_rev >= 2) {
|
|
|
|
phy812_val = bwi_phy812_value(mac, 0x101);
|
|
|
|
PHY_WRITE(mac, 0x812, phy812_val);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x15, 0xafb0);
|
|
|
|
}
|
|
|
|
|
|
|
|
++test;
|
|
|
|
test >>= 8;
|
|
|
|
|
|
|
|
if (test > test_lim)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i > 15)
|
|
|
|
rf->rf_calib = rfr_78;
|
|
|
|
else
|
|
|
|
rf->rf_calib = calib;
|
|
|
|
if (rf->rf_calib != 0xffff) {
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_INIT,
|
|
|
|
"RF calibration value: 0x%04x\n", rf->rf_calib);
|
|
|
|
rf->rf_flags |= BWI_RF_F_INITED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore trashes registers
|
|
|
|
*/
|
|
|
|
PHY_WRITE(mac, save_phy_regs_comm[0], save_phy_comm[0]);
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i) {
|
|
|
|
int pos = (i + 1) % SAVE_RF_MAX;
|
|
|
|
|
|
|
|
RF_WRITE(mac, save_rf_regs[pos], save_rf[pos]);
|
|
|
|
}
|
|
|
|
for (i = 1; i < SAVE_PHY_COMM_MAX; ++i)
|
|
|
|
PHY_WRITE(mac, save_phy_regs_comm[i], save_phy_comm[i]);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, bbp_atten);
|
|
|
|
if (phy->phy_version != 0)
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN_EX, rf_chan_ex);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x35, phyr_35);
|
|
|
|
bwi_rf_work_around(mac, rf->rf_curchan);
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11B) {
|
|
|
|
PHY_WRITE(mac, 0x30, phyr_30);
|
|
|
|
CSR_WRITE_2(sc, BWI_BPHY_CTRL, bphy_ctrl);
|
|
|
|
} else if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) {
|
|
|
|
/* XXX Spec only says when PHY is linked (gmode) */
|
|
|
|
CSR_CLRBITS_2(sc, BWI_RF_ANTDIV, 0x8000);
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_PHY_11G_MAX; ++i) {
|
|
|
|
PHY_WRITE(mac, save_phy_regs_11g[i],
|
|
|
|
save_phy_11g[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x80f, phyr_80f);
|
|
|
|
PHY_WRITE(mac, 0x810, phyr_810);
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef SAVE_PHY_11G_MAX
|
|
|
|
#undef SAVE_PHY_COMM_MAX
|
|
|
|
#undef SAVE_RF_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwi_rf_calibval(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
/* http://bcm-specs.sipsolutions.net/RCCTable */
|
|
|
|
static const uint16_t rf_calibvals[] = {
|
|
|
|
0x2, 0x3, 0x1, 0xf, 0x6, 0x7, 0x5, 0xf,
|
|
|
|
0xa, 0xb, 0x9, 0xf, 0xe, 0xf, 0xd, 0xf
|
|
|
|
};
|
|
|
|
uint16_t val, calib;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
val = RF_READ(mac, BWI_RFR_BBP_ATTEN);
|
|
|
|
idx = __SHIFTOUT(val, BWI_RFR_BBP_ATTEN_CALIB_IDX);
|
2016-04-19 23:37:24 +00:00
|
|
|
KASSERT(idx < (int)nitems(rf_calibvals), ("idx %d", idx));
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
|
|
|
|
calib = rf_calibvals[idx] << 1;
|
|
|
|
if (val & BWI_RFR_BBP_ATTEN_CALIB_BIT)
|
|
|
|
calib |= 0x1;
|
|
|
|
calib |= 0x20;
|
|
|
|
|
|
|
|
return calib;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline int32_t
|
|
|
|
_bwi_adjust_devide(int32_t num, int32_t den)
|
|
|
|
{
|
|
|
|
if (num < 0)
|
|
|
|
return (num / den);
|
|
|
|
else
|
|
|
|
return (num + den / 2) / den;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table
|
|
|
|
* "calculating table entries"
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_txpower(int8_t *txpwr, uint8_t idx, const int16_t pa_params[])
|
|
|
|
{
|
|
|
|
int32_t m1, m2, f, dbm;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
m1 = _bwi_adjust_devide(16 * pa_params[0] + idx * pa_params[1], 32);
|
|
|
|
m2 = imax(_bwi_adjust_devide(32768 + idx * pa_params[2], 256), 1);
|
|
|
|
|
|
|
|
#define ITER_MAX 16
|
|
|
|
|
|
|
|
f = 256;
|
|
|
|
for (i = 0; i < ITER_MAX; ++i) {
|
|
|
|
int32_t q, d;
|
|
|
|
|
|
|
|
q = _bwi_adjust_devide(
|
|
|
|
f * 4096 - _bwi_adjust_devide(m2 * f, 16) * f, 2048);
|
|
|
|
d = abs(q - f);
|
|
|
|
f = q;
|
|
|
|
|
|
|
|
if (d < 2)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == ITER_MAX)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
#undef ITER_MAX
|
|
|
|
|
|
|
|
dbm = _bwi_adjust_devide(m1 * f, 8192);
|
|
|
|
if (dbm < -127)
|
|
|
|
dbm = -127;
|
|
|
|
else if (dbm > 128)
|
|
|
|
dbm = 128;
|
|
|
|
|
|
|
|
*txpwr = dbm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bwi_rf_map_txpower(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;
|
|
|
|
uint16_t sprom_ofs, val, mask;
|
|
|
|
int16_t pa_params[3];
|
|
|
|
int error = 0, i, ant_gain, reg_txpower_max;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out max TX power
|
|
|
|
*/
|
|
|
|
val = bwi_read_sprom(sc, BWI_SPROM_MAX_TXPWR);
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A) {
|
|
|
|
rf->rf_txpower_max = __SHIFTOUT(val,
|
|
|
|
BWI_SPROM_MAX_TXPWR_MASK_11A);
|
|
|
|
} else {
|
|
|
|
rf->rf_txpower_max = __SHIFTOUT(val,
|
|
|
|
BWI_SPROM_MAX_TXPWR_MASK_11BG);
|
|
|
|
|
|
|
|
if ((sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) &&
|
|
|
|
phy->phy_mode == IEEE80211_MODE_11G)
|
|
|
|
rf->rf_txpower_max -= 3;
|
|
|
|
}
|
|
|
|
if (rf->rf_txpower_max <= 0) {
|
|
|
|
device_printf(sc->sc_dev, "invalid max txpower in sprom\n");
|
|
|
|
rf->rf_txpower_max = 74;
|
|
|
|
}
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"max txpower from sprom: %d dBm\n", rf->rf_txpower_max);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out region/domain max TX power, which is adjusted
|
|
|
|
* by antenna gain and 1.5 dBm fluctuation as mentioned
|
|
|
|
* in v3 spec.
|
|
|
|
*/
|
|
|
|
val = bwi_read_sprom(sc, BWI_SPROM_ANT_GAIN);
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A)
|
|
|
|
ant_gain = __SHIFTOUT(val, BWI_SPROM_ANT_GAIN_MASK_11A);
|
|
|
|
else
|
|
|
|
ant_gain = __SHIFTOUT(val, BWI_SPROM_ANT_GAIN_MASK_11BG);
|
|
|
|
if (ant_gain == 0xff) {
|
|
|
|
device_printf(sc->sc_dev, "invalid antenna gain in sprom\n");
|
|
|
|
ant_gain = 2;
|
|
|
|
}
|
|
|
|
ant_gain *= 4;
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"ant gain %d dBm\n", ant_gain);
|
|
|
|
|
|
|
|
reg_txpower_max = 90 - ant_gain - 6; /* XXX magic number */
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"region/domain max txpower %d dBm\n", reg_txpower_max);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force max TX power within region/domain TX power limit
|
|
|
|
*/
|
|
|
|
if (rf->rf_txpower_max > reg_txpower_max)
|
|
|
|
rf->rf_txpower_max = reg_txpower_max;
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"max txpower %d dBm\n", rf->rf_txpower_max);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create TSSI to TX power mapping
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (sc->sc_bbp_id == BWI_BBPID_BCM4301 &&
|
|
|
|
rf->rf_type != BWI_RF_T_BCM2050) {
|
|
|
|
rf->rf_idle_tssi0 = BWI_DEFAULT_IDLE_TSSI;
|
|
|
|
bcopy(bwi_txpower_map_11b, rf->rf_txpower_map0,
|
|
|
|
sizeof(rf->rf_txpower_map0));
|
|
|
|
goto back;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define IS_VALID_PA_PARAM(p) ((p) != 0 && (p) != -1)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract PA parameters
|
|
|
|
*/
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A)
|
|
|
|
sprom_ofs = BWI_SPROM_PA_PARAM_11A;
|
|
|
|
else
|
|
|
|
sprom_ofs = BWI_SPROM_PA_PARAM_11BG;
|
2015-09-22 02:44:59 +00:00
|
|
|
for (i = 0; i < nitems(pa_params); ++i)
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
pa_params[i] = (int16_t)bwi_read_sprom(sc, sprom_ofs + (i * 2));
|
|
|
|
|
2015-09-22 02:44:59 +00:00
|
|
|
for (i = 0; i < nitems(pa_params); ++i) {
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
/*
|
|
|
|
* If one of the PA parameters from SPROM is not valid,
|
|
|
|
* fall back to the default values, if there are any.
|
|
|
|
*/
|
|
|
|
if (!IS_VALID_PA_PARAM(pa_params[i])) {
|
|
|
|
const int8_t *txpower_map;
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"no tssi2dbm table for 11a PHY\n");
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11G) {
|
|
|
|
DPRINTF(sc,
|
|
|
|
BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"%s\n", "use default 11g TSSI map");
|
|
|
|
txpower_map = bwi_txpower_map_11g;
|
|
|
|
} else {
|
|
|
|
DPRINTF(sc,
|
|
|
|
BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"%s\n", "use default 11b TSSI map");
|
|
|
|
txpower_map = bwi_txpower_map_11b;
|
|
|
|
}
|
|
|
|
|
|
|
|
rf->rf_idle_tssi0 = BWI_DEFAULT_IDLE_TSSI;
|
|
|
|
bcopy(txpower_map, rf->rf_txpower_map0,
|
|
|
|
sizeof(rf->rf_txpower_map0));
|
|
|
|
goto back;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All of the PA parameters from SPROM are valid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract idle TSSI from SPROM.
|
|
|
|
*/
|
|
|
|
val = bwi_read_sprom(sc, BWI_SPROM_IDLE_TSSI);
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"sprom idle tssi: 0x%04x\n", val);
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A)
|
|
|
|
mask = BWI_SPROM_IDLE_TSSI_MASK_11A;
|
|
|
|
else
|
|
|
|
mask = BWI_SPROM_IDLE_TSSI_MASK_11BG;
|
|
|
|
|
|
|
|
rf->rf_idle_tssi0 = (int)__SHIFTOUT(val, mask);
|
|
|
|
if (!IS_VALID_PA_PARAM(rf->rf_idle_tssi0))
|
|
|
|
rf->rf_idle_tssi0 = 62;
|
|
|
|
|
|
|
|
#undef IS_VALID_PA_PARAM
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate TX power map, which is indexed by TSSI
|
|
|
|
*/
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_ATTACH | BWI_DBG_TXPOWER,
|
|
|
|
"%s\n", "TSSI-TX power map:");
|
|
|
|
for (i = 0; i < BWI_TSSI_MAX; ++i) {
|
|
|
|
error = bwi_rf_calc_txpower(&rf->rf_txpower_map0[i], i,
|
|
|
|
pa_params);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"bwi_rf_calc_txpower failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BWI_DEBUG
|
|
|
|
if (i != 0 && i % 8 == 0) {
|
|
|
|
_DPRINTF(sc,
|
|
|
|
BWI_DBG_RF | BWI_DBG_ATTACH | BWI_DBG_TXPOWER,
|
|
|
|
"%s\n", "");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
_DPRINTF(sc, BWI_DBG_RF | BWI_DBG_ATTACH | BWI_DBG_TXPOWER,
|
|
|
|
"%d ", rf->rf_txpower_map0[i]);
|
|
|
|
}
|
|
|
|
_DPRINTF(sc, BWI_DBG_RF | BWI_DBG_ATTACH | BWI_DBG_TXPOWER,
|
|
|
|
"%s\n", "");
|
|
|
|
back:
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_TXPOWER | BWI_DBG_ATTACH,
|
|
|
|
"idle tssi0: %d\n", rf->rf_idle_tssi0);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_lo_update_11g(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;
|
|
|
|
struct rf_saveregs regs;
|
|
|
|
uint16_t ant_div, chan_ex;
|
|
|
|
uint8_t devi_ctrl;
|
|
|
|
u_int orig_chan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save RF/PHY registers for later restoration
|
|
|
|
*/
|
|
|
|
orig_chan = rf->rf_curchan;
|
|
|
|
bzero(®s, sizeof(regs));
|
|
|
|
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
SAVE_PHY_REG(mac, ®s, 429);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 802);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x429, regs.phy_429 & 0x7fff);
|
|
|
|
PHY_WRITE(mac, 0x802, regs.phy_802 & 0xfffc);
|
|
|
|
}
|
|
|
|
|
|
|
|
ant_div = CSR_READ_2(sc, BWI_RF_ANTDIV);
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_ANTDIV, ant_div | 0x8000);
|
|
|
|
chan_ex = CSR_READ_2(sc, BWI_RF_CHAN_EX);
|
|
|
|
|
|
|
|
SAVE_PHY_REG(mac, ®s, 15);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 2a);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 35);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 60);
|
|
|
|
SAVE_RF_REG(mac, ®s, 43);
|
|
|
|
SAVE_RF_REG(mac, ®s, 7a);
|
|
|
|
SAVE_RF_REG(mac, ®s, 52);
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
SAVE_PHY_REG(mac, ®s, 811);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 812);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 814);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 815);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force to channel 6 */
|
|
|
|
bwi_rf_set_chan(mac, 6, 0);
|
|
|
|
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
PHY_WRITE(mac, 0x429, regs.phy_429 & 0x7fff);
|
|
|
|
PHY_WRITE(mac, 0x802, regs.phy_802 & 0xfffc);
|
|
|
|
bwi_mac_dummy_xmit(mac);
|
|
|
|
}
|
|
|
|
RF_WRITE(mac, 0x43, 0x6);
|
|
|
|
|
|
|
|
bwi_phy_set_bbp_atten(mac, 2);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN_EX, 0);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x2e, 0x7f);
|
|
|
|
PHY_WRITE(mac, 0x80f, 0x78);
|
|
|
|
PHY_WRITE(mac, 0x35, regs.phy_35 & 0xff7f);
|
|
|
|
RF_WRITE(mac, 0x7a, regs.rf_7a & 0xfff0);
|
|
|
|
PHY_WRITE(mac, 0x2b, 0x203);
|
|
|
|
PHY_WRITE(mac, 0x2a, 0x8a3);
|
|
|
|
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
PHY_WRITE(mac, 0x814, regs.phy_814 | 0x3);
|
|
|
|
PHY_WRITE(mac, 0x815, regs.phy_815 & 0xfffc);
|
|
|
|
PHY_WRITE(mac, 0x811, 0x1b3);
|
|
|
|
PHY_WRITE(mac, 0x812, 0xb2);
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
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.
2015-08-27 08:56:39 +00:00
|
|
|
if ((sc->sc_flags & BWI_F_RUNNING) == 0)
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
tpctl->tp_ctrl2 = bwi_rf_get_tp_ctrl2(mac);
|
|
|
|
PHY_WRITE(mac, 0x80f, 0x8078);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Measure all RF LO
|
|
|
|
*/
|
|
|
|
devi_ctrl = _bwi_rf_lo_update_11g(mac, regs.rf_7a);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore saved RF/PHY registers
|
|
|
|
*/
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
PHY_WRITE(mac, 0x15, 0xe300);
|
|
|
|
PHY_WRITE(mac, 0x812, (devi_ctrl << 8) | 0xa0);
|
|
|
|
DELAY(5);
|
|
|
|
PHY_WRITE(mac, 0x812, (devi_ctrl << 8) | 0xa2);
|
|
|
|
DELAY(2);
|
|
|
|
PHY_WRITE(mac, 0x812, (devi_ctrl << 8) | 0xa3);
|
|
|
|
} else {
|
|
|
|
PHY_WRITE(mac, 0x15, devi_ctrl | 0xefa0);
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
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.
2015-08-27 08:56:39 +00:00
|
|
|
if ((sc->sc_flags & BWI_F_RUNNING) == 0)
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
tpctl = NULL;
|
|
|
|
bwi_rf_lo_adjust(mac, tpctl);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x2e, 0x807f);
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED)
|
|
|
|
PHY_WRITE(mac, 0x2f, 0x202);
|
|
|
|
else
|
|
|
|
PHY_WRITE(mac, 0x2f, 0x101);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN_EX, chan_ex);
|
|
|
|
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 15);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 2a);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 35);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 60);
|
|
|
|
|
|
|
|
RESTORE_RF_REG(mac, ®s, 43);
|
|
|
|
RESTORE_RF_REG(mac, ®s, 7a);
|
|
|
|
|
|
|
|
regs.rf_52 &= 0xf0;
|
|
|
|
regs.rf_52 |= (RF_READ(mac, 0x52) & 0xf);
|
|
|
|
RF_WRITE(mac, 0x52, regs.rf_52);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_ANTDIV, ant_div);
|
|
|
|
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 811);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 812);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 814);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 815);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 429);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 802);
|
|
|
|
}
|
|
|
|
|
|
|
|
bwi_rf_set_chan(mac, orig_chan, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
bwi_rf_lo_devi_measure(struct bwi_mac *mac, uint16_t ctrl)
|
|
|
|
{
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
uint32_t devi = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED)
|
|
|
|
ctrl <<= 8;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; ++i) {
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED) {
|
|
|
|
PHY_WRITE(mac, 0x15, 0xe300);
|
|
|
|
PHY_WRITE(mac, 0x812, ctrl | 0xb0);
|
|
|
|
DELAY(5);
|
|
|
|
PHY_WRITE(mac, 0x812, ctrl | 0xb2);
|
|
|
|
DELAY(2);
|
|
|
|
PHY_WRITE(mac, 0x812, ctrl | 0xb3);
|
|
|
|
DELAY(4);
|
|
|
|
PHY_WRITE(mac, 0x15, 0xf300);
|
|
|
|
} else {
|
|
|
|
PHY_WRITE(mac, 0x15, ctrl | 0xefa0);
|
|
|
|
DELAY(2);
|
|
|
|
PHY_WRITE(mac, 0x15, ctrl | 0xefe0);
|
|
|
|
DELAY(4);
|
|
|
|
PHY_WRITE(mac, 0x15, ctrl | 0xffe0);
|
|
|
|
}
|
|
|
|
DELAY(8);
|
|
|
|
devi += PHY_READ(mac, 0x2d);
|
|
|
|
}
|
|
|
|
return devi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwi_rf_get_tp_ctrl2(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
uint32_t devi_min;
|
|
|
|
uint16_t tp_ctrl2 = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
RF_WRITE(mac, 0x52, 0);
|
|
|
|
DELAY(10);
|
|
|
|
devi_min = bwi_rf_lo_devi_measure(mac, 0);
|
|
|
|
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
|
|
uint32_t devi;
|
|
|
|
|
|
|
|
RF_WRITE(mac, 0x52, i);
|
|
|
|
DELAY(10);
|
|
|
|
devi = bwi_rf_lo_devi_measure(mac, 0);
|
|
|
|
|
|
|
|
if (devi < devi_min) {
|
|
|
|
devi_min = devi;
|
|
|
|
tp_ctrl2 = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tp_ctrl2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
_bwi_rf_lo_update_11g(struct bwi_mac *mac, uint16_t orig_rf7a)
|
|
|
|
{
|
|
|
|
#define RF_ATTEN_LISTSZ 14
|
|
|
|
#define BBP_ATTEN_MAX 4 /* half */
|
|
|
|
|
|
|
|
static const int rf_atten_list[RF_ATTEN_LISTSZ] =
|
|
|
|
{ 3, 1, 5, 7, 9, 2, 0, 4, 6, 8, 1, 2, 3, 4 };
|
|
|
|
static const int rf_atten_init_list[RF_ATTEN_LISTSZ] =
|
|
|
|
{ 0, 3, 1, 5, 7, 3, 2, 0, 4, 6, -1, -1, -1, -1 };
|
|
|
|
static const int rf_lo_measure_order[RF_ATTEN_LISTSZ] =
|
|
|
|
{ 3, 1, 5, 7, 9, 2, 0, 4, 6, 8, 10, 11, 12, 13 };
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
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.
2015-08-27 08:56:39 +00:00
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
struct bwi_rf_lo lo_save, *lo;
|
|
|
|
uint8_t devi_ctrl = 0;
|
|
|
|
int idx, adj_rf7a = 0;
|
|
|
|
|
|
|
|
bzero(&lo_save, sizeof(lo_save));
|
|
|
|
for (idx = 0; idx < RF_ATTEN_LISTSZ; ++idx) {
|
|
|
|
int init_rf_atten = rf_atten_init_list[idx];
|
|
|
|
int rf_atten = rf_atten_list[idx];
|
|
|
|
int bbp_atten;
|
|
|
|
|
|
|
|
for (bbp_atten = 0; bbp_atten < BBP_ATTEN_MAX; ++bbp_atten) {
|
|
|
|
uint16_t tp_ctrl2, rf7a;
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
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.
2015-08-27 08:56:39 +00:00
|
|
|
if ((sc->sc_flags & BWI_F_RUNNING) == 0) {
|
Bring in Andrew Thompson's port of Sepherosa Ziehau's bwi driver for
Broadcom BCM43xx chipsets. This driver uses the v3 firmware that
needs to be fetched separately. A port will be committed to create
the bwi firmware module.
The driver matches the following chips: Broadcom BCM4301, BCM4307,
BCM4306, BCM4309, BCM4311, BCM4312, BCM4318, BCM4319
The driver works for 802.11b and 802.11g.
Limitations:
This doesn't support the 802.11a or 802.11n portion of radios.
Some BCM4306 and BCM4309 cards don't work with Channel 1, 2 or 3.
Documenation for this firmware is reverse engineered from
http://bcm.sipsolutions.net/
V4 of the firmware is needed for 11a or 11n support
http://bcm-v4.sipsolutions.net/
Firmware needs to be fetched from a third party, port to be committed
# I've tested this with a BCM4319 mini-pci and a BCM4318 CardBus card, and
# not connected it to the build until the firmware port is committed.
Obtained from: DragonFlyBSD, //depot/projects/vap
Reviewed by: sam@, thompsa@
2009-05-03 04:01:43 +00:00
|
|
|
if (idx == 0) {
|
|
|
|
bzero(&lo_save, sizeof(lo_save));
|
|
|
|
} else if (init_rf_atten < 0) {
|
|
|
|
lo = bwi_get_rf_lo(mac,
|
|
|
|
rf_atten, 2 * bbp_atten);
|
|
|
|
bcopy(lo, &lo_save, sizeof(lo_save));
|
|
|
|
} else {
|
|
|
|
lo = bwi_get_rf_lo(mac,
|
|
|
|
init_rf_atten, 0);
|
|
|
|
bcopy(lo, &lo_save, sizeof(lo_save));
|
|
|
|
}
|
|
|
|
|
|
|
|
devi_ctrl = 0;
|
|
|
|
adj_rf7a = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* Linux driver overflows 'val'
|
|
|
|
*/
|
|
|
|
if (init_rf_atten >= 0) {
|
|
|
|
int val;
|
|
|
|
|
|
|
|
val = rf_atten * 2 + bbp_atten;
|
|
|
|
if (val > 14) {
|
|
|
|
adj_rf7a = 1;
|
|
|
|
if (val > 17)
|
|
|
|
devi_ctrl = 1;
|
|
|
|
if (val > 19)
|
|
|
|
devi_ctrl = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lo = bwi_get_rf_lo(mac,
|
|
|
|
rf_atten, 2 * bbp_atten);
|
|
|
|
if (!bwi_rf_lo_isused(mac, lo))
|
|
|
|
continue;
|
|
|
|
bcopy(lo, &lo_save, sizeof(lo_save));
|
|
|
|
|
|
|
|
devi_ctrl = 3;
|
|
|
|
adj_rf7a = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
RF_WRITE(mac, BWI_RFR_ATTEN, rf_atten);
|
|
|
|
|
|
|
|
tp_ctrl2 = mac->mac_tpctl.tp_ctrl2;
|
|
|
|
if (init_rf_atten < 0)
|
|
|
|
tp_ctrl2 |= (3 << 4);
|
|
|
|
RF_WRITE(mac, BWI_RFR_TXPWR, tp_ctrl2);
|
|
|
|
|
|
|
|
DELAY(10);
|
|
|
|
|
|
|
|
bwi_phy_set_bbp_atten(mac, bbp_atten * 2);
|
|
|
|
|
|
|
|
rf7a = orig_rf7a & 0xfff0;
|
|
|
|
if (adj_rf7a)
|
|
|
|
rf7a |= 0x8;
|
|
|
|
RF_WRITE(mac, 0x7a, rf7a);
|
|
|
|
|
|
|
|
lo = bwi_get_rf_lo(mac,
|
|
|
|
rf_lo_measure_order[idx], bbp_atten * 2);
|
|
|
|
bwi_rf_lo_measure_11g(mac, &lo_save, lo, devi_ctrl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return devi_ctrl;
|
|
|
|
|
|
|
|
#undef RF_ATTEN_LISTSZ
|
|
|
|
#undef BBP_ATTEN_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_lo_measure_11g(struct bwi_mac *mac, const struct bwi_rf_lo *src_lo,
|
|
|
|
struct bwi_rf_lo *dst_lo, uint8_t devi_ctrl)
|
|
|
|
{
|
|
|
|
#define LO_ADJUST_MIN 1
|
|
|
|
#define LO_ADJUST_MAX 8
|
|
|
|
#define LO_ADJUST(hi, lo) { .ctrl_hi = hi, .ctrl_lo = lo }
|
|
|
|
static const struct bwi_rf_lo rf_lo_adjust[LO_ADJUST_MAX] = {
|
|
|
|
LO_ADJUST(1, 1),
|
|
|
|
LO_ADJUST(1, 0),
|
|
|
|
LO_ADJUST(1, -1),
|
|
|
|
LO_ADJUST(0, -1),
|
|
|
|
LO_ADJUST(-1, -1),
|
|
|
|
LO_ADJUST(-1, 0),
|
|
|
|
LO_ADJUST(-1, 1),
|
|
|
|
LO_ADJUST(0, 1)
|
|
|
|
};
|
|
|
|
#undef LO_ADJUST
|
|
|
|
|
|
|
|
struct bwi_rf_lo lo_min;
|
|
|
|
uint32_t devi_min;
|
|
|
|
int found, loop_count, adjust_state;
|
|
|
|
|
|
|
|
bcopy(src_lo, &lo_min, sizeof(lo_min));
|
|
|
|
RF_LO_WRITE(mac, &lo_min);
|
|
|
|
devi_min = bwi_rf_lo_devi_measure(mac, devi_ctrl);
|
|
|
|
|
|
|
|
loop_count = 12; /* XXX */
|
|
|
|
adjust_state = 0;
|
|
|
|
do {
|
|
|
|
struct bwi_rf_lo lo_base;
|
|
|
|
int i, fin;
|
|
|
|
|
|
|
|
found = 0;
|
|
|
|
if (adjust_state == 0) {
|
|
|
|
i = LO_ADJUST_MIN;
|
|
|
|
fin = LO_ADJUST_MAX;
|
|
|
|
} else if (adjust_state % 2 == 0) {
|
|
|
|
i = adjust_state - 1;
|
|
|
|
fin = adjust_state + 1;
|
|
|
|
} else {
|
|
|
|
i = adjust_state - 2;
|
|
|
|
fin = adjust_state + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < LO_ADJUST_MIN)
|
|
|
|
i += LO_ADJUST_MAX;
|
|
|
|
KASSERT(i <= LO_ADJUST_MAX && i >= LO_ADJUST_MIN, ("i %d", i));
|
|
|
|
|
|
|
|
if (fin > LO_ADJUST_MAX)
|
|
|
|
fin -= LO_ADJUST_MAX;
|
|
|
|
KASSERT(fin <= LO_ADJUST_MAX && fin >= LO_ADJUST_MIN,
|
|
|
|
("fin %d", fin));
|
|
|
|
|
|
|
|
bcopy(&lo_min, &lo_base, sizeof(lo_base));
|
|
|
|
for (;;) {
|
|
|
|
struct bwi_rf_lo lo;
|
|
|
|
|
|
|
|
lo.ctrl_hi = lo_base.ctrl_hi +
|
|
|
|
rf_lo_adjust[i - 1].ctrl_hi;
|
|
|
|
lo.ctrl_lo = lo_base.ctrl_lo +
|
|
|
|
rf_lo_adjust[i - 1].ctrl_lo;
|
|
|
|
|
|
|
|
if (abs(lo.ctrl_lo) < 9 && abs(lo.ctrl_hi) < 9) {
|
|
|
|
uint32_t devi;
|
|
|
|
|
|
|
|
RF_LO_WRITE(mac, &lo);
|
|
|
|
devi = bwi_rf_lo_devi_measure(mac, devi_ctrl);
|
|
|
|
if (devi < devi_min) {
|
|
|
|
devi_min = devi;
|
|
|
|
adjust_state = i;
|
|
|
|
found = 1;
|
|
|
|
bcopy(&lo, &lo_min, sizeof(lo_min));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == fin)
|
|
|
|
break;
|
|
|
|
if (i == LO_ADJUST_MAX)
|
|
|
|
i = LO_ADJUST_MIN;
|
|
|
|
else
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
} while (loop_count-- && found);
|
|
|
|
|
|
|
|
bcopy(&lo_min, dst_lo, sizeof(*dst_lo));
|
|
|
|
|
|
|
|
#undef LO_ADJUST_MIN
|
|
|
|
#undef LO_ADJUST_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_calc_nrssi_slope_11b(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
#define SAVE_RF_MAX 3
|
|
|
|
#define SAVE_PHY_MAX 8
|
|
|
|
|
|
|
|
static const uint16_t save_rf_regs[SAVE_RF_MAX] =
|
|
|
|
{ 0x7a, 0x52, 0x43 };
|
|
|
|
static const uint16_t save_phy_regs[SAVE_PHY_MAX] =
|
|
|
|
{ 0x30, 0x26, 0x15, 0x2a, 0x20, 0x5a, 0x59, 0x58 };
|
|
|
|
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
uint16_t save_rf[SAVE_RF_MAX];
|
|
|
|
uint16_t save_phy[SAVE_PHY_MAX];
|
|
|
|
uint16_t ant_div, bbp_atten, chan_ex;
|
|
|
|
int16_t nrssi[2];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save RF/PHY registers for later restoration
|
|
|
|
*/
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
save_rf[i] = RF_READ(mac, save_rf_regs[i]);
|
|
|
|
for (i = 0; i < SAVE_PHY_MAX; ++i)
|
|
|
|
save_phy[i] = PHY_READ(mac, save_phy_regs[i]);
|
|
|
|
|
|
|
|
ant_div = CSR_READ_2(sc, BWI_RF_ANTDIV);
|
|
|
|
bbp_atten = CSR_READ_2(sc, BWI_BBP_ATTEN);
|
|
|
|
chan_ex = CSR_READ_2(sc, BWI_RF_CHAN_EX);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate nrssi0
|
|
|
|
*/
|
|
|
|
if (phy->phy_rev >= 5)
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xff80);
|
|
|
|
else
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xfff0);
|
|
|
|
PHY_WRITE(mac, 0x30, 0xff);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_BPHY_CTRL, 0x7f7f);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x26, 0);
|
|
|
|
PHY_SETBITS(mac, 0x15, 0x20);
|
|
|
|
PHY_WRITE(mac, 0x2a, 0x8a3);
|
|
|
|
RF_SETBITS(mac, 0x7a, 0x80);
|
|
|
|
|
|
|
|
nrssi[0] = (int16_t)PHY_READ(mac, 0x27);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate nrssi1
|
|
|
|
*/
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xff80);
|
|
|
|
if (phy->phy_version >= 2)
|
|
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0x40);
|
|
|
|
else if (phy->phy_version == 0)
|
|
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0x122);
|
|
|
|
else
|
|
|
|
CSR_CLRBITS_2(sc, BWI_RF_CHAN_EX, 0xdfff);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x20, 0x3f3f);
|
|
|
|
PHY_WRITE(mac, 0x15, 0xf330);
|
|
|
|
|
|
|
|
RF_WRITE(mac, 0x5a, 0x60);
|
|
|
|
RF_CLRBITS(mac, 0x43, 0xff0f);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x5a, 0x480);
|
|
|
|
PHY_WRITE(mac, 0x59, 0x810);
|
|
|
|
PHY_WRITE(mac, 0x58, 0xd);
|
|
|
|
|
|
|
|
DELAY(20);
|
|
|
|
|
|
|
|
nrssi[1] = (int16_t)PHY_READ(mac, 0x27);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore saved RF/PHY registers
|
|
|
|
*/
|
|
|
|
PHY_WRITE(mac, save_phy_regs[0], save_phy[0]);
|
|
|
|
RF_WRITE(mac, save_rf_regs[0], save_rf[0]);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_ANTDIV, ant_div);
|
|
|
|
|
|
|
|
for (i = 1; i < 4; ++i)
|
|
|
|
PHY_WRITE(mac, save_phy_regs[i], save_phy[i]);
|
|
|
|
|
|
|
|
bwi_rf_work_around(mac, rf->rf_curchan);
|
|
|
|
|
|
|
|
if (phy->phy_version != 0)
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN_EX, chan_ex);
|
|
|
|
|
|
|
|
for (; i < SAVE_PHY_MAX; ++i)
|
|
|
|
PHY_WRITE(mac, save_phy_regs[i], save_phy[i]);
|
|
|
|
|
|
|
|
for (i = 1; i < SAVE_RF_MAX; ++i)
|
|
|
|
RF_WRITE(mac, save_rf_regs[i], save_rf[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Install calculated narrow RSSI values
|
|
|
|
*/
|
|
|
|
if (nrssi[0] == nrssi[1])
|
|
|
|
rf->rf_nrssi_slope = 0x10000;
|
|
|
|
else
|
|
|
|
rf->rf_nrssi_slope = 0x400000 / (nrssi[0] - nrssi[1]);
|
|
|
|
if (nrssi[0] <= -4) {
|
|
|
|
rf->rf_nrssi[0] = nrssi[0];
|
|
|
|
rf->rf_nrssi[1] = nrssi[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef SAVE_RF_MAX
|
|
|
|
#undef SAVE_PHY_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_set_nrssi_ofs_11g(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
#define SAVE_RF_MAX 2
|
|
|
|
#define SAVE_PHY_COMM_MAX 10
|
|
|
|
#define SAVE_PHY6_MAX 8
|
|
|
|
|
|
|
|
static const uint16_t save_rf_regs[SAVE_RF_MAX] =
|
|
|
|
{ 0x7a, 0x43 };
|
|
|
|
static const uint16_t save_phy_comm_regs[SAVE_PHY_COMM_MAX] = {
|
|
|
|
0x0001, 0x0811, 0x0812, 0x0814,
|
|
|
|
0x0815, 0x005a, 0x0059, 0x0058,
|
|
|
|
0x000a, 0x0003
|
|
|
|
};
|
|
|
|
static const uint16_t save_phy6_regs[SAVE_PHY6_MAX] = {
|
|
|
|
0x002e, 0x002f, 0x080f, 0x0810,
|
|
|
|
0x0801, 0x0060, 0x0014, 0x0478
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
uint16_t save_rf[SAVE_RF_MAX];
|
|
|
|
uint16_t save_phy_comm[SAVE_PHY_COMM_MAX];
|
|
|
|
uint16_t save_phy6[SAVE_PHY6_MAX];
|
|
|
|
uint16_t rf7b = 0xffff;
|
|
|
|
int16_t nrssi;
|
|
|
|
int i, phy6_idx = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_PHY_COMM_MAX; ++i)
|
|
|
|
save_phy_comm[i] = PHY_READ(mac, save_phy_comm_regs[i]);
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
save_rf[i] = RF_READ(mac, save_rf_regs[i]);
|
|
|
|
|
|
|
|
PHY_CLRBITS(mac, 0x429, 0x8000);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x1, 0x3fff, 0x4000);
|
|
|
|
PHY_SETBITS(mac, 0x811, 0xc);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x812, 0xfff3, 0x4);
|
|
|
|
PHY_CLRBITS(mac, 0x802, 0x3);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 6) {
|
|
|
|
for (i = 0; i < SAVE_PHY6_MAX; ++i)
|
|
|
|
save_phy6[i] = PHY_READ(mac, save_phy6_regs[i]);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x2e, 0);
|
|
|
|
PHY_WRITE(mac, 0x2f, 0);
|
|
|
|
PHY_WRITE(mac, 0x80f, 0);
|
|
|
|
PHY_WRITE(mac, 0x810, 0);
|
|
|
|
PHY_SETBITS(mac, 0x478, 0x100);
|
|
|
|
PHY_SETBITS(mac, 0x801, 0x40);
|
|
|
|
PHY_SETBITS(mac, 0x60, 0x40);
|
|
|
|
PHY_SETBITS(mac, 0x14, 0x200);
|
|
|
|
}
|
|
|
|
|
|
|
|
RF_SETBITS(mac, 0x7a, 0x70);
|
|
|
|
RF_SETBITS(mac, 0x7a, 0x80);
|
|
|
|
|
|
|
|
DELAY(30);
|
|
|
|
|
|
|
|
nrssi = bwi_nrssi_11g(mac);
|
|
|
|
if (nrssi == 31) {
|
|
|
|
for (i = 7; i >= 4; --i) {
|
|
|
|
RF_WRITE(mac, 0x7b, i);
|
|
|
|
DELAY(20);
|
|
|
|
nrssi = bwi_nrssi_11g(mac);
|
|
|
|
if (nrssi < 31 && rf7b == 0xffff)
|
|
|
|
rf7b = i;
|
|
|
|
}
|
|
|
|
if (rf7b == 0xffff)
|
|
|
|
rf7b = 4;
|
|
|
|
} else {
|
|
|
|
struct bwi_gains gains;
|
|
|
|
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xff80);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x814, 0x1);
|
|
|
|
PHY_CLRBITS(mac, 0x815, 0x1);
|
|
|
|
PHY_SETBITS(mac, 0x811, 0xc);
|
|
|
|
PHY_SETBITS(mac, 0x812, 0xc);
|
|
|
|
PHY_SETBITS(mac, 0x811, 0x30);
|
|
|
|
PHY_SETBITS(mac, 0x812, 0x30);
|
|
|
|
PHY_WRITE(mac, 0x5a, 0x480);
|
|
|
|
PHY_WRITE(mac, 0x59, 0x810);
|
|
|
|
PHY_WRITE(mac, 0x58, 0xd);
|
|
|
|
if (phy->phy_version == 0)
|
|
|
|
PHY_WRITE(mac, 0x3, 0x122);
|
|
|
|
else
|
|
|
|
PHY_SETBITS(mac, 0xa, 0x2000);
|
|
|
|
PHY_SETBITS(mac, 0x814, 0x4);
|
|
|
|
PHY_CLRBITS(mac, 0x815, 0x4);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x3, 0xff9f, 0x40);
|
|
|
|
RF_SETBITS(mac, 0x7a, 0xf);
|
|
|
|
|
|
|
|
bzero(&gains, sizeof(gains));
|
|
|
|
gains.tbl_gain1 = 3;
|
|
|
|
gains.tbl_gain2 = 0;
|
|
|
|
gains.phy_gain = 1;
|
|
|
|
bwi_set_gains(mac, &gains);
|
|
|
|
|
|
|
|
RF_FILT_SETBITS(mac, 0x43, 0xf0, 0xf);
|
|
|
|
DELAY(30);
|
|
|
|
|
|
|
|
nrssi = bwi_nrssi_11g(mac);
|
|
|
|
if (nrssi == -32) {
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
RF_WRITE(mac, 0x7b, i);
|
|
|
|
DELAY(20);
|
|
|
|
nrssi = bwi_nrssi_11g(mac);
|
|
|
|
if (nrssi > -31 && rf7b == 0xffff)
|
|
|
|
rf7b = i;
|
|
|
|
}
|
|
|
|
if (rf7b == 0xffff)
|
|
|
|
rf7b = 3;
|
|
|
|
} else {
|
|
|
|
rf7b = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RF_WRITE(mac, 0x7b, rf7b);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore saved RF/PHY registers
|
|
|
|
*/
|
|
|
|
if (phy->phy_rev >= 6) {
|
|
|
|
for (phy6_idx = 0; phy6_idx < 4; ++phy6_idx) {
|
|
|
|
PHY_WRITE(mac, save_phy6_regs[phy6_idx],
|
|
|
|
save_phy6[phy6_idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Saved PHY registers 0, 1, 2 are handled later */
|
|
|
|
for (i = 3; i < SAVE_PHY_COMM_MAX; ++i)
|
|
|
|
PHY_WRITE(mac, save_phy_comm_regs[i], save_phy_comm[i]);
|
|
|
|
|
|
|
|
for (i = SAVE_RF_MAX - 1; i >= 0; --i)
|
|
|
|
RF_WRITE(mac, save_rf_regs[i], save_rf[i]);
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x802, 0x3);
|
|
|
|
PHY_SETBITS(mac, 0x429, 0x8000);
|
|
|
|
|
|
|
|
bwi_set_gains(mac, NULL);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 6) {
|
|
|
|
for (; phy6_idx < SAVE_PHY6_MAX; ++phy6_idx) {
|
|
|
|
PHY_WRITE(mac, save_phy6_regs[phy6_idx],
|
|
|
|
save_phy6[phy6_idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_WRITE(mac, save_phy_comm_regs[0], save_phy_comm[0]);
|
|
|
|
PHY_WRITE(mac, save_phy_comm_regs[2], save_phy_comm[2]);
|
|
|
|
PHY_WRITE(mac, save_phy_comm_regs[1], save_phy_comm[1]);
|
|
|
|
|
|
|
|
#undef SAVE_RF_MAX
|
|
|
|
#undef SAVE_PHY_COMM_MAX
|
|
|
|
#undef SAVE_PHY6_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_calc_nrssi_slope_11g(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
#define SAVE_RF_MAX 3
|
|
|
|
#define SAVE_PHY_COMM_MAX 4
|
|
|
|
#define SAVE_PHY3_MAX 8
|
|
|
|
|
|
|
|
static const uint16_t save_rf_regs[SAVE_RF_MAX] =
|
|
|
|
{ 0x7a, 0x52, 0x43 };
|
|
|
|
static const uint16_t save_phy_comm_regs[SAVE_PHY_COMM_MAX] =
|
|
|
|
{ 0x15, 0x5a, 0x59, 0x58 };
|
|
|
|
static const uint16_t save_phy3_regs[SAVE_PHY3_MAX] = {
|
|
|
|
0x002e, 0x002f, 0x080f, 0x0810,
|
|
|
|
0x0801, 0x0060, 0x0014, 0x0478
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
uint16_t save_rf[SAVE_RF_MAX];
|
|
|
|
uint16_t save_phy_comm[SAVE_PHY_COMM_MAX];
|
|
|
|
uint16_t save_phy3[SAVE_PHY3_MAX];
|
|
|
|
uint16_t ant_div, bbp_atten, chan_ex;
|
|
|
|
struct bwi_gains gains;
|
|
|
|
int16_t nrssi[2];
|
|
|
|
int i, phy3_idx = 0;
|
|
|
|
|
|
|
|
if (rf->rf_rev >= 9)
|
|
|
|
return;
|
|
|
|
else if (rf->rf_rev == 8)
|
|
|
|
bwi_rf_set_nrssi_ofs_11g(mac);
|
|
|
|
|
|
|
|
PHY_CLRBITS(mac, 0x429, 0x8000);
|
|
|
|
PHY_CLRBITS(mac, 0x802, 0x3);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save RF/PHY registers for later restoration
|
|
|
|
*/
|
|
|
|
ant_div = CSR_READ_2(sc, BWI_RF_ANTDIV);
|
|
|
|
CSR_SETBITS_2(sc, BWI_RF_ANTDIV, 0x8000);
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
save_rf[i] = RF_READ(mac, save_rf_regs[i]);
|
|
|
|
for (i = 0; i < SAVE_PHY_COMM_MAX; ++i)
|
|
|
|
save_phy_comm[i] = PHY_READ(mac, save_phy_comm_regs[i]);
|
|
|
|
|
|
|
|
bbp_atten = CSR_READ_2(sc, BWI_BBP_ATTEN);
|
|
|
|
chan_ex = CSR_READ_2(sc, BWI_RF_CHAN_EX);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 3) {
|
|
|
|
for (i = 0; i < SAVE_PHY3_MAX; ++i)
|
|
|
|
save_phy3[i] = PHY_READ(mac, save_phy3_regs[i]);
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x2e, 0);
|
|
|
|
PHY_WRITE(mac, 0x810, 0);
|
|
|
|
|
|
|
|
if (phy->phy_rev == 4 || phy->phy_rev == 6 ||
|
|
|
|
phy->phy_rev == 7) {
|
|
|
|
PHY_SETBITS(mac, 0x478, 0x100);
|
|
|
|
PHY_SETBITS(mac, 0x810, 0x40);
|
|
|
|
} else if (phy->phy_rev == 3 || phy->phy_rev == 5) {
|
|
|
|
PHY_CLRBITS(mac, 0x810, 0x40);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_SETBITS(mac, 0x60, 0x40);
|
|
|
|
PHY_SETBITS(mac, 0x14, 0x200);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate nrssi0
|
|
|
|
*/
|
|
|
|
RF_SETBITS(mac, 0x7a, 0x70);
|
|
|
|
|
|
|
|
bzero(&gains, sizeof(gains));
|
|
|
|
gains.tbl_gain1 = 0;
|
|
|
|
gains.tbl_gain2 = 8;
|
|
|
|
gains.phy_gain = 0;
|
|
|
|
bwi_set_gains(mac, &gains);
|
|
|
|
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xff08);
|
|
|
|
if (phy->phy_rev >= 2) {
|
|
|
|
PHY_FILT_SETBITS(mac, 0x811, 0xffcf, 0x30);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x812, 0xffcf, 0x10);
|
|
|
|
}
|
|
|
|
|
|
|
|
RF_SETBITS(mac, 0x7a, 0x80);
|
|
|
|
DELAY(20);
|
|
|
|
nrssi[0] = bwi_nrssi_11g(mac);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate nrssi1
|
|
|
|
*/
|
|
|
|
RF_CLRBITS(mac, 0x7a, 0xff80);
|
|
|
|
if (phy->phy_version >= 2)
|
|
|
|
PHY_FILT_SETBITS(mac, 0x3, 0xff9f, 0x40);
|
|
|
|
CSR_SETBITS_2(sc, BWI_RF_CHAN_EX, 0x2000);
|
|
|
|
|
|
|
|
RF_SETBITS(mac, 0x7a, 0xf);
|
|
|
|
PHY_WRITE(mac, 0x15, 0xf330);
|
|
|
|
if (phy->phy_rev >= 2) {
|
|
|
|
PHY_FILT_SETBITS(mac, 0x812, 0xffcf, 0x20);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x811, 0xffcf, 0x20);
|
|
|
|
}
|
|
|
|
|
|
|
|
bzero(&gains, sizeof(gains));
|
|
|
|
gains.tbl_gain1 = 3;
|
|
|
|
gains.tbl_gain2 = 0;
|
|
|
|
gains.phy_gain = 1;
|
|
|
|
bwi_set_gains(mac, &gains);
|
|
|
|
|
|
|
|
if (rf->rf_rev == 8) {
|
|
|
|
RF_WRITE(mac, 0x43, 0x1f);
|
|
|
|
} else {
|
|
|
|
RF_FILT_SETBITS(mac, 0x52, 0xff0f, 0x60);
|
|
|
|
RF_FILT_SETBITS(mac, 0x43, 0xfff0, 0x9);
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x5a, 0x480);
|
|
|
|
PHY_WRITE(mac, 0x59, 0x810);
|
|
|
|
PHY_WRITE(mac, 0x58, 0xd);
|
|
|
|
DELAY(20);
|
|
|
|
|
|
|
|
nrssi[1] = bwi_nrssi_11g(mac);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Install calculated narrow RSSI values
|
|
|
|
*/
|
|
|
|
if (nrssi[1] == nrssi[0])
|
|
|
|
rf->rf_nrssi_slope = 0x10000;
|
|
|
|
else
|
|
|
|
rf->rf_nrssi_slope = 0x400000 / (nrssi[0] - nrssi[1]);
|
|
|
|
if (nrssi[0] >= -4) {
|
|
|
|
rf->rf_nrssi[0] = nrssi[1];
|
|
|
|
rf->rf_nrssi[1] = nrssi[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore saved RF/PHY registers
|
|
|
|
*/
|
|
|
|
if (phy->phy_rev >= 3) {
|
|
|
|
for (phy3_idx = 0; phy3_idx < 4; ++phy3_idx) {
|
|
|
|
PHY_WRITE(mac, save_phy3_regs[phy3_idx],
|
|
|
|
save_phy3[phy3_idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (phy->phy_rev >= 2) {
|
|
|
|
PHY_CLRBITS(mac, 0x812, 0x30);
|
|
|
|
PHY_CLRBITS(mac, 0x811, 0x30);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_RF_MAX; ++i)
|
|
|
|
RF_WRITE(mac, save_rf_regs[i], save_rf[i]);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_ANTDIV, ant_div);
|
|
|
|
CSR_WRITE_2(sc, BWI_BBP_ATTEN, bbp_atten);
|
|
|
|
CSR_WRITE_2(sc, BWI_RF_CHAN_EX, chan_ex);
|
|
|
|
|
|
|
|
for (i = 0; i < SAVE_PHY_COMM_MAX; ++i)
|
|
|
|
PHY_WRITE(mac, save_phy_comm_regs[i], save_phy_comm[i]);
|
|
|
|
|
|
|
|
bwi_rf_work_around(mac, rf->rf_curchan);
|
|
|
|
PHY_SETBITS(mac, 0x802, 0x3);
|
|
|
|
bwi_set_gains(mac, NULL);
|
|
|
|
PHY_SETBITS(mac, 0x429, 0x8000);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 3) {
|
|
|
|
for (; phy3_idx < SAVE_PHY3_MAX; ++phy3_idx) {
|
|
|
|
PHY_WRITE(mac, save_phy3_regs[phy3_idx],
|
|
|
|
save_phy3[phy3_idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bwi_rf_init_sw_nrssi_table(mac);
|
|
|
|
bwi_rf_set_nrssi_thr_11g(mac);
|
|
|
|
|
|
|
|
#undef SAVE_RF_MAX
|
|
|
|
#undef SAVE_PHY_COMM_MAX
|
|
|
|
#undef SAVE_PHY3_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_init_sw_nrssi_table(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
int d, i;
|
|
|
|
|
|
|
|
d = 0x1f - rf->rf_nrssi[0];
|
|
|
|
for (i = 0; i < BWI_NRSSI_TBLSZ; ++i) {
|
|
|
|
int val;
|
|
|
|
|
|
|
|
val = (((i - d) * rf->rf_nrssi_slope) / 0x10000) + 0x3a;
|
|
|
|
if (val < 0)
|
|
|
|
val = 0;
|
|
|
|
else if (val > 0x3f)
|
|
|
|
val = 0x3f;
|
|
|
|
|
|
|
|
rf->rf_nrssi_table[i] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_init_hw_nrssi_table(struct bwi_mac *mac, uint16_t adjust)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < BWI_NRSSI_TBLSZ; ++i) {
|
|
|
|
int16_t val;
|
|
|
|
|
|
|
|
val = bwi_nrssi_read(mac, i);
|
|
|
|
|
|
|
|
val -= adjust;
|
|
|
|
if (val < -32)
|
|
|
|
val = -32;
|
|
|
|
else if (val > 31)
|
|
|
|
val = 31;
|
|
|
|
|
|
|
|
bwi_nrssi_write(mac, i, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_set_nrssi_thr_11b(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
int32_t thr;
|
|
|
|
|
|
|
|
if (rf->rf_type != BWI_RF_T_BCM2050 ||
|
|
|
|
(mac->mac_sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate nrssi threshold
|
|
|
|
*/
|
|
|
|
if (rf->rf_rev >= 6) {
|
|
|
|
thr = (rf->rf_nrssi[1] - rf->rf_nrssi[0]) * 32;
|
|
|
|
thr += 20 * (rf->rf_nrssi[0] + 1);
|
|
|
|
thr /= 40;
|
|
|
|
} else {
|
|
|
|
thr = rf->rf_nrssi[1] - 5;
|
|
|
|
}
|
|
|
|
if (thr < 0)
|
|
|
|
thr = 0;
|
|
|
|
else if (thr > 0x3e)
|
|
|
|
thr = 0x3e;
|
|
|
|
|
|
|
|
PHY_READ(mac, BWI_PHYR_NRSSI_THR_11B); /* dummy read */
|
|
|
|
PHY_WRITE(mac, BWI_PHYR_NRSSI_THR_11B, (((uint16_t)thr) << 8) | 0x1c);
|
|
|
|
|
|
|
|
if (rf->rf_rev >= 6) {
|
|
|
|
PHY_WRITE(mac, 0x87, 0xe0d);
|
|
|
|
PHY_WRITE(mac, 0x86, 0xc0b);
|
|
|
|
PHY_WRITE(mac, 0x85, 0xa09);
|
|
|
|
PHY_WRITE(mac, 0x84, 0x808);
|
|
|
|
PHY_WRITE(mac, 0x83, 0x808);
|
|
|
|
PHY_WRITE(mac, 0x82, 0x604);
|
|
|
|
PHY_WRITE(mac, 0x81, 0x302);
|
|
|
|
PHY_WRITE(mac, 0x80, 0x100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline int32_t
|
|
|
|
_nrssi_threshold(const struct bwi_rf *rf, int32_t val)
|
|
|
|
{
|
|
|
|
val *= (rf->rf_nrssi[1] - rf->rf_nrssi[0]);
|
|
|
|
val += (rf->rf_nrssi[0] << 6);
|
|
|
|
if (val < 32)
|
|
|
|
val += 31;
|
|
|
|
else
|
|
|
|
val += 32;
|
|
|
|
val >>= 6;
|
|
|
|
if (val < -31)
|
|
|
|
val = -31;
|
|
|
|
else if (val > 31)
|
|
|
|
val = 31;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_set_nrssi_thr_11g(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
int32_t thr1, thr2;
|
|
|
|
uint16_t thr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the two nrssi thresholds
|
|
|
|
*/
|
|
|
|
if ((mac->mac_phy.phy_flags & BWI_PHY_F_LINKED) == 0 ||
|
|
|
|
(mac->mac_sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) == 0) {
|
|
|
|
int16_t nrssi;
|
|
|
|
|
|
|
|
nrssi = bwi_nrssi_read(mac, 0x20);
|
|
|
|
if (nrssi >= 32)
|
|
|
|
nrssi -= 64;
|
|
|
|
|
|
|
|
if (nrssi < 3) {
|
|
|
|
thr1 = 0x2b;
|
|
|
|
thr2 = 0x27;
|
|
|
|
} else {
|
|
|
|
thr1 = 0x2d;
|
|
|
|
thr2 = 0x2b;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* TODO Interfere mode */
|
|
|
|
thr1 = _nrssi_threshold(&mac->mac_rf, 0x11);
|
|
|
|
thr2 = _nrssi_threshold(&mac->mac_rf, 0xe);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NRSSI_THR1_MASK __BITS(5, 0)
|
|
|
|
#define NRSSI_THR2_MASK __BITS(11, 6)
|
|
|
|
|
|
|
|
thr = __SHIFTIN((uint32_t)thr1, NRSSI_THR1_MASK) |
|
|
|
|
__SHIFTIN((uint32_t)thr2, NRSSI_THR2_MASK);
|
|
|
|
PHY_FILT_SETBITS(mac, BWI_PHYR_NRSSI_THR_11G, 0xf000, thr);
|
|
|
|
|
|
|
|
#undef NRSSI_THR1_MASK
|
|
|
|
#undef NRSSI_THR2_MASK
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_clear_tssi(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
/* XXX use function pointer */
|
|
|
|
if (mac->mac_phy.phy_mode == IEEE80211_MODE_11A) {
|
|
|
|
/* TODO:11A */
|
|
|
|
} else {
|
|
|
|
uint16_t val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
val = __SHIFTIN(BWI_INVALID_TSSI, BWI_LO_TSSI_MASK) |
|
|
|
|
__SHIFTIN(BWI_INVALID_TSSI, BWI_HI_TSSI_MASK);
|
|
|
|
|
|
|
|
for (i = 0; i < 2; ++i) {
|
|
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ,
|
|
|
|
BWI_COMM_MOBJ_TSSI_DS + (i * 2), val);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 2; ++i) {
|
|
|
|
MOBJ_WRITE_2(mac, BWI_COMM_MOBJ,
|
|
|
|
BWI_COMM_MOBJ_TSSI_OFDM + (i * 2), val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_clear_state(struct bwi_rf *rf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
rf->rf_flags &= ~BWI_RF_CLEAR_FLAGS;
|
|
|
|
bzero(rf->rf_lo, sizeof(rf->rf_lo));
|
|
|
|
bzero(rf->rf_lo_used, sizeof(rf->rf_lo_used));
|
|
|
|
|
|
|
|
rf->rf_nrssi_slope = 0;
|
|
|
|
rf->rf_nrssi[0] = BWI_INVALID_NRSSI;
|
|
|
|
rf->rf_nrssi[1] = BWI_INVALID_NRSSI;
|
|
|
|
|
|
|
|
for (i = 0; i < BWI_NRSSI_TBLSZ; ++i)
|
|
|
|
rf->rf_nrssi_table[i] = i;
|
|
|
|
|
|
|
|
rf->rf_lo_gain = 0;
|
|
|
|
rf->rf_rx_gain = 0;
|
|
|
|
|
|
|
|
bcopy(rf->rf_txpower_map0, rf->rf_txpower_map,
|
|
|
|
sizeof(rf->rf_txpower_map));
|
|
|
|
rf->rf_idle_tssi = rf->rf_idle_tssi0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_on_11a(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
/* TODO:11A */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_on_11bg(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x15, 0x8000);
|
|
|
|
PHY_WRITE(mac, 0x15, 0xcc00);
|
|
|
|
if (phy->phy_flags & BWI_PHY_F_LINKED)
|
|
|
|
PHY_WRITE(mac, 0x15, 0xc0);
|
|
|
|
else
|
|
|
|
PHY_WRITE(mac, 0x15, 0);
|
|
|
|
|
|
|
|
bwi_rf_set_chan(mac, 6 /* XXX */, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bwi_rf_set_ant_mode(struct bwi_mac *mac, int ant_mode)
|
|
|
|
{
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_phy *phy = &mac->mac_phy;
|
|
|
|
uint16_t val;
|
|
|
|
|
|
|
|
KASSERT(ant_mode == BWI_ANT_MODE_0 ||
|
|
|
|
ant_mode == BWI_ANT_MODE_1 ||
|
|
|
|
ant_mode == BWI_ANT_MODE_AUTO, ("ant_mode %d", ant_mode));
|
|
|
|
|
|
|
|
HFLAGS_CLRBITS(mac, BWI_HFLAG_AUTO_ANTDIV);
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11B) {
|
|
|
|
/* NOTE: v4/v3 conflicts, take v3 */
|
|
|
|
if (mac->mac_rev == 2)
|
|
|
|
val = BWI_ANT_MODE_AUTO;
|
|
|
|
else
|
|
|
|
val = ant_mode;
|
|
|
|
val <<= 7;
|
|
|
|
PHY_FILT_SETBITS(mac, 0x3e2, 0xfe7f, val);
|
|
|
|
} else { /* 11a/g */
|
|
|
|
/* XXX reg/value naming */
|
|
|
|
val = ant_mode << 7;
|
|
|
|
PHY_FILT_SETBITS(mac, 0x401, 0x7e7f, val);
|
|
|
|
|
|
|
|
if (ant_mode == BWI_ANT_MODE_AUTO)
|
|
|
|
PHY_CLRBITS(mac, 0x42b, 0x100);
|
|
|
|
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11A) {
|
|
|
|
/* TODO:11A */
|
|
|
|
} else { /* 11g */
|
|
|
|
if (ant_mode == BWI_ANT_MODE_AUTO)
|
|
|
|
PHY_SETBITS(mac, 0x48c, 0x2000);
|
|
|
|
else
|
|
|
|
PHY_CLRBITS(mac, 0x48c, 0x2000);
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 2) {
|
|
|
|
PHY_SETBITS(mac, 0x461, 0x10);
|
|
|
|
PHY_FILT_SETBITS(mac, 0x4ad, 0xff00, 0x15);
|
|
|
|
if (phy->phy_rev == 2) {
|
|
|
|
PHY_WRITE(mac, 0x427, 0x8);
|
|
|
|
} else {
|
|
|
|
PHY_FILT_SETBITS(mac, 0x427,
|
|
|
|
0xff00, 0x8);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phy->phy_rev >= 6)
|
|
|
|
PHY_WRITE(mac, 0x49b, 0xdc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX v4 set AUTO_ANTDIV unconditionally */
|
|
|
|
if (ant_mode == BWI_ANT_MODE_AUTO)
|
|
|
|
HFLAGS_SETBITS(mac, BWI_HFLAG_AUTO_ANTDIV);
|
|
|
|
|
|
|
|
val = ant_mode << 8;
|
|
|
|
MOBJ_FILT_SETBITS_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_TX_BEACON,
|
|
|
|
0xfc3f, val);
|
|
|
|
MOBJ_FILT_SETBITS_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_TX_ACK,
|
|
|
|
0xfc3f, val);
|
|
|
|
MOBJ_FILT_SETBITS_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_TX_PROBE_RESP,
|
|
|
|
0xfc3f, val);
|
|
|
|
|
|
|
|
/* XXX what's these */
|
|
|
|
if (phy->phy_mode == IEEE80211_MODE_11B)
|
|
|
|
CSR_SETBITS_2(sc, 0x5e, 0x4);
|
|
|
|
|
|
|
|
CSR_WRITE_4(sc, 0x100, 0x1000000);
|
|
|
|
if (mac->mac_rev < 5)
|
|
|
|
CSR_WRITE_4(sc, 0x10c, 0x1000000);
|
|
|
|
|
|
|
|
mac->mac_rf.rf_ant_mode = ant_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bwi_rf_get_latest_tssi(struct bwi_mac *mac, int8_t tssi[], uint16_t ofs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ) {
|
|
|
|
uint16_t val;
|
|
|
|
|
|
|
|
val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, ofs + i);
|
|
|
|
tssi[i++] = (int8_t)__SHIFTOUT(val, BWI_LO_TSSI_MASK);
|
|
|
|
tssi[i++] = (int8_t)__SHIFTOUT(val, BWI_HI_TSSI_MASK);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
if (tssi[i] == BWI_INVALID_TSSI)
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bwi_rf_tssi2dbm(struct bwi_mac *mac, int8_t tssi, int8_t *txpwr)
|
|
|
|
{
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
int pwr_idx;
|
|
|
|
|
|
|
|
pwr_idx = rf->rf_idle_tssi + (int)tssi - rf->rf_base_tssi;
|
|
|
|
#if 0
|
|
|
|
if (pwr_idx < 0 || pwr_idx >= BWI_TSSI_MAX)
|
|
|
|
return EINVAL;
|
|
|
|
#else
|
|
|
|
if (pwr_idx < 0)
|
|
|
|
pwr_idx = 0;
|
|
|
|
else if (pwr_idx >= BWI_TSSI_MAX)
|
|
|
|
pwr_idx = BWI_TSSI_MAX - 1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
*txpwr = rf->rf_txpower_map[pwr_idx];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_rssi_bcm2050(struct bwi_mac *mac, const struct bwi_rxbuf_hdr *hdr)
|
|
|
|
{
|
|
|
|
uint16_t flags1, flags3;
|
|
|
|
int rssi, lna_gain;
|
|
|
|
|
|
|
|
rssi = hdr->rxh_rssi;
|
|
|
|
flags1 = le16toh(hdr->rxh_flags1);
|
|
|
|
flags3 = le16toh(hdr->rxh_flags3);
|
|
|
|
|
|
|
|
if (flags1 & BWI_RXH_F1_OFDM) {
|
|
|
|
if (rssi > 127)
|
|
|
|
rssi -= 256;
|
|
|
|
if (flags3 & BWI_RXH_F3_BCM2050_RSSI)
|
|
|
|
rssi += 17;
|
|
|
|
else
|
|
|
|
rssi -= 4;
|
|
|
|
return rssi;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac->mac_sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) {
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
|
|
|
|
if (rssi >= BWI_NRSSI_TBLSZ)
|
|
|
|
rssi = BWI_NRSSI_TBLSZ - 1;
|
|
|
|
|
|
|
|
rssi = ((31 - (int)rf->rf_nrssi_table[rssi]) * -131) / 128;
|
|
|
|
rssi -= 67;
|
|
|
|
} else {
|
|
|
|
rssi = ((31 - rssi) * -149) / 128;
|
|
|
|
rssi -= 68;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac->mac_phy.phy_mode != IEEE80211_MODE_11G)
|
|
|
|
return rssi;
|
|
|
|
|
|
|
|
if (flags3 & BWI_RXH_F3_BCM2050_RSSI)
|
|
|
|
rssi += 20;
|
|
|
|
|
|
|
|
lna_gain = __SHIFTOUT(le16toh(hdr->rxh_phyinfo),
|
|
|
|
BWI_RXH_PHYINFO_LNAGAIN);
|
|
|
|
DPRINTF(mac->mac_sc, BWI_DBG_RF | BWI_DBG_RX,
|
|
|
|
"lna_gain %d, phyinfo 0x%04x\n",
|
|
|
|
lna_gain, le16toh(hdr->rxh_phyinfo));
|
|
|
|
switch (lna_gain) {
|
|
|
|
case 0:
|
|
|
|
rssi += 27;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
rssi += 6;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rssi += 12;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* According to v3 spec, we should do _nothing_ here,
|
|
|
|
* but it seems that the result RSSI will be too low
|
|
|
|
* (relative to what ath(4) says). Raise it a little
|
|
|
|
* bit.
|
|
|
|
*/
|
|
|
|
rssi += 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("impossible lna gain %d", lna_gain);
|
|
|
|
}
|
|
|
|
return rssi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_rssi_bcm2053(struct bwi_mac *mac, const struct bwi_rxbuf_hdr *hdr)
|
|
|
|
{
|
|
|
|
uint16_t flags1;
|
|
|
|
int rssi;
|
|
|
|
|
|
|
|
rssi = (((int)hdr->rxh_rssi - 11) * 103) / 64;
|
|
|
|
|
|
|
|
flags1 = le16toh(hdr->rxh_flags1);
|
|
|
|
if (flags1 & BWI_RXH_F1_BCM2053_RSSI)
|
|
|
|
rssi -= 109;
|
|
|
|
else
|
|
|
|
rssi -= 83;
|
|
|
|
return rssi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_rssi_bcm2060(struct bwi_mac *mac, const struct bwi_rxbuf_hdr *hdr)
|
|
|
|
{
|
|
|
|
int rssi;
|
|
|
|
|
|
|
|
rssi = hdr->rxh_rssi;
|
|
|
|
if (rssi > 127)
|
|
|
|
rssi -= 256;
|
|
|
|
return rssi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_noise_bcm2050(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
uint16_t val;
|
|
|
|
int noise;
|
|
|
|
|
|
|
|
val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_RF_NOISE);
|
|
|
|
noise = (int)val; /* XXX check bounds? */
|
|
|
|
|
|
|
|
if (mac->mac_sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) {
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
|
|
|
|
if (noise >= BWI_NRSSI_TBLSZ)
|
|
|
|
noise = BWI_NRSSI_TBLSZ - 1;
|
|
|
|
|
|
|
|
noise = ((31 - (int)rf->rf_nrssi_table[noise]) * -131) / 128;
|
|
|
|
noise -= 67;
|
|
|
|
} else {
|
|
|
|
noise = ((31 - noise) * -149) / 128;
|
|
|
|
noise -= 68;
|
|
|
|
}
|
|
|
|
return noise;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_noise_bcm2053(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
uint16_t val;
|
|
|
|
int noise;
|
|
|
|
|
|
|
|
val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_RF_NOISE);
|
|
|
|
noise = (int)val; /* XXX check bounds? */
|
|
|
|
|
|
|
|
noise = ((noise - 11) * 103) / 64;
|
|
|
|
noise -= 109;
|
|
|
|
return noise;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwi_rf_calc_noise_bcm2060(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
/* XXX Dont know how to calc */
|
|
|
|
return (BWI_NOISE_FLOOR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwi_rf_lo_measure_11b(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
uint16_t val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
val = 0;
|
|
|
|
for (i = 0; i < 10; ++i) {
|
|
|
|
PHY_WRITE(mac, 0x15, 0xafa0);
|
|
|
|
DELAY(1);
|
|
|
|
PHY_WRITE(mac, 0x15, 0xefa0);
|
|
|
|
DELAY(10);
|
|
|
|
PHY_WRITE(mac, 0x15, 0xffa0);
|
|
|
|
DELAY(40);
|
|
|
|
|
|
|
|
val += PHY_READ(mac, 0x2c);
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwi_rf_lo_update_11b(struct bwi_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwi_softc *sc = mac->mac_sc;
|
|
|
|
struct bwi_rf *rf = &mac->mac_rf;
|
|
|
|
struct rf_saveregs regs;
|
|
|
|
uint16_t rf_val, phy_val, min_val, val;
|
|
|
|
uint16_t rf52, bphy_ctrl;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DPRINTF(sc, BWI_DBG_RF | BWI_DBG_INIT, "%s enter\n", __func__);
|
|
|
|
|
|
|
|
bzero(®s, sizeof(regs));
|
|
|
|
bphy_ctrl = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save RF/PHY registers for later restoration
|
|
|
|
*/
|
|
|
|
SAVE_PHY_REG(mac, ®s, 15);
|
|
|
|
rf52 = RF_READ(mac, 0x52) & 0xfff0;
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2050) {
|
|
|
|
SAVE_PHY_REG(mac, ®s, 0a);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 2a);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 35);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 03);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 01);
|
|
|
|
SAVE_PHY_REG(mac, ®s, 30);
|
|
|
|
|
|
|
|
SAVE_RF_REG(mac, ®s, 43);
|
|
|
|
SAVE_RF_REG(mac, ®s, 7a);
|
|
|
|
|
|
|
|
bphy_ctrl = CSR_READ_2(sc, BWI_BPHY_CTRL);
|
|
|
|
|
|
|
|
SAVE_RF_REG(mac, ®s, 52);
|
|
|
|
regs.rf_52 &= 0xf0;
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x30, 0xff);
|
|
|
|
CSR_WRITE_2(sc, BWI_PHY_CTRL, 0x3f3f);
|
|
|
|
PHY_WRITE(mac, 0x35, regs.phy_35 & 0xff7f);
|
|
|
|
RF_WRITE(mac, 0x7a, regs.rf_7a & 0xfff0);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHY_WRITE(mac, 0x15, 0xb000);
|
|
|
|
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2050) {
|
|
|
|
PHY_WRITE(mac, 0x2b, 0x203);
|
|
|
|
PHY_WRITE(mac, 0x2a, 0x8a3);
|
|
|
|
} else {
|
|
|
|
PHY_WRITE(mac, 0x2b, 0x1402);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup RF signal
|
|
|
|
*/
|
|
|
|
rf_val = 0;
|
|
|
|
min_val = UINT16_MAX;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
RF_WRITE(mac, 0x52, rf52 | i);
|
|
|
|
bwi_rf_lo_measure_11b(mac); /* Ignore return value */
|
|
|
|
}
|
|
|
|
for (i = 0; i < 10; ++i) {
|
|
|
|
RF_WRITE(mac, 0x52, rf52 | i);
|
|
|
|
|
|
|
|
val = bwi_rf_lo_measure_11b(mac) / 10;
|
|
|
|
if (val < min_val) {
|
|
|
|
min_val = val;
|
|
|
|
rf_val = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RF_WRITE(mac, 0x52, rf52 | rf_val);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup PHY signal
|
|
|
|
*/
|
|
|
|
phy_val = 0;
|
|
|
|
min_val = UINT16_MAX;
|
|
|
|
|
|
|
|
for (i = -4; i < 5; i += 2) {
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (j = -4; j < 5; j += 2) {
|
|
|
|
uint16_t phy2f;
|
|
|
|
|
|
|
|
phy2f = (0x100 * i) + j;
|
|
|
|
if (j < 0)
|
|
|
|
phy2f += 0x100;
|
|
|
|
PHY_WRITE(mac, 0x2f, phy2f);
|
|
|
|
|
|
|
|
val = bwi_rf_lo_measure_11b(mac) / 10;
|
|
|
|
if (val < min_val) {
|
|
|
|
min_val = val;
|
|
|
|
phy_val = phy2f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PHY_WRITE(mac, 0x2f, phy_val + 0x101);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore saved RF/PHY registers
|
|
|
|
*/
|
|
|
|
if (rf->rf_type == BWI_RF_T_BCM2050) {
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 0a);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 2a);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 35);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 03);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 01);
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 30);
|
|
|
|
|
|
|
|
RESTORE_RF_REG(mac, ®s, 43);
|
|
|
|
RESTORE_RF_REG(mac, ®s, 7a);
|
|
|
|
|
|
|
|
RF_FILT_SETBITS(mac, 0x52, 0xf, regs.rf_52);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, BWI_BPHY_CTRL, bphy_ctrl);
|
|
|
|
}
|
|
|
|
RESTORE_PHY_REG(mac, ®s, 15);
|
|
|
|
|
|
|
|
bwi_rf_work_around(mac, rf->rf_curchan);
|
|
|
|
}
|