112e36acbSWarner Losh /* 212e36acbSWarner Losh * Copyright (c) 2007 The DragonFly Project. All rights reserved. 312e36acbSWarner Losh * 412e36acbSWarner Losh * This code is derived from software contributed to The DragonFly Project 512e36acbSWarner Losh * by Sepherosa Ziehau <sepherosa@gmail.com> 612e36acbSWarner Losh * 712e36acbSWarner Losh * Redistribution and use in source and binary forms, with or without 812e36acbSWarner Losh * modification, are permitted provided that the following conditions 912e36acbSWarner Losh * are met: 1012e36acbSWarner Losh * 1112e36acbSWarner Losh * 1. Redistributions of source code must retain the above copyright 1212e36acbSWarner Losh * notice, this list of conditions and the following disclaimer. 1312e36acbSWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 1412e36acbSWarner Losh * notice, this list of conditions and the following disclaimer in 1512e36acbSWarner Losh * the documentation and/or other materials provided with the 1612e36acbSWarner Losh * distribution. 1712e36acbSWarner Losh * 3. Neither the name of The DragonFly Project nor the names of its 1812e36acbSWarner Losh * contributors may be used to endorse or promote products derived 1912e36acbSWarner Losh * from this software without specific, prior written permission. 2012e36acbSWarner Losh * 2112e36acbSWarner Losh * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2212e36acbSWarner Losh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2312e36acbSWarner Losh * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2412e36acbSWarner Losh * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2512e36acbSWarner Losh * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2612e36acbSWarner Losh * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2712e36acbSWarner Losh * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2812e36acbSWarner Losh * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2912e36acbSWarner Losh * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3012e36acbSWarner Losh * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3112e36acbSWarner Losh * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3212e36acbSWarner Losh * SUCH DAMAGE. 3312e36acbSWarner Losh * 3412e36acbSWarner Losh * $DragonFly: src/sys/dev/netif/bwi/bwiphy.c,v 1.5 2008/01/15 09:01:13 sephe Exp $ 3512e36acbSWarner Losh */ 3612e36acbSWarner Losh 3712e36acbSWarner Losh #include <sys/cdefs.h> 3812e36acbSWarner Losh __FBSDID("$FreeBSD$"); 3912e36acbSWarner Losh 4012e36acbSWarner Losh #include "opt_inet.h" 4112e36acbSWarner Losh 4212e36acbSWarner Losh #include <sys/param.h> 4312e36acbSWarner Losh #include <sys/endian.h> 4412e36acbSWarner Losh #include <sys/kernel.h> 4512e36acbSWarner Losh #include <sys/bus.h> 4612e36acbSWarner Losh #include <sys/malloc.h> 4712e36acbSWarner Losh #include <sys/proc.h> 4812e36acbSWarner Losh #include <sys/rman.h> 4912e36acbSWarner Losh #include <sys/socket.h> 5012e36acbSWarner Losh #include <sys/sockio.h> 5112e36acbSWarner Losh #include <sys/sysctl.h> 5212e36acbSWarner Losh #include <sys/systm.h> 5312e36acbSWarner Losh 5412e36acbSWarner Losh #include <net/if.h> 5512e36acbSWarner Losh #include <net/if_dl.h> 5612e36acbSWarner Losh #include <net/if_media.h> 5712e36acbSWarner Losh #include <net/if_types.h> 5812e36acbSWarner Losh #include <net/if_arp.h> 5912e36acbSWarner Losh #include <net/ethernet.h> 6012e36acbSWarner Losh #include <net/if_llc.h> 6112e36acbSWarner Losh 6212e36acbSWarner Losh #include <net80211/ieee80211_var.h> 6312e36acbSWarner Losh #include <net80211/ieee80211_radiotap.h> 6412e36acbSWarner Losh #include <net80211/ieee80211_amrr.h> 6512e36acbSWarner Losh 6612e36acbSWarner Losh #include <machine/bus.h> 6712e36acbSWarner Losh 6812e36acbSWarner Losh #include <dev/bwi/bitops.h> 6912e36acbSWarner Losh #include <dev/bwi/if_bwireg.h> 7012e36acbSWarner Losh #include <dev/bwi/if_bwivar.h> 7112e36acbSWarner Losh #include <dev/bwi/bwimac.h> 7212e36acbSWarner Losh #include <dev/bwi/bwirf.h> 7312e36acbSWarner Losh #include <dev/bwi/bwiphy.h> 7412e36acbSWarner Losh 7512e36acbSWarner Losh static void bwi_phy_init_11a(struct bwi_mac *); 7612e36acbSWarner Losh static void bwi_phy_init_11g(struct bwi_mac *); 7712e36acbSWarner Losh static void bwi_phy_init_11b_rev2(struct bwi_mac *); 7812e36acbSWarner Losh static void bwi_phy_init_11b_rev4(struct bwi_mac *); 7912e36acbSWarner Losh static void bwi_phy_init_11b_rev5(struct bwi_mac *); 8012e36acbSWarner Losh static void bwi_phy_init_11b_rev6(struct bwi_mac *); 8112e36acbSWarner Losh 8212e36acbSWarner Losh static void bwi_phy_config_11g(struct bwi_mac *); 8312e36acbSWarner Losh static void bwi_phy_config_agc(struct bwi_mac *); 8412e36acbSWarner Losh 8512e36acbSWarner Losh static void bwi_tbl_write_2(struct bwi_mac *mac, uint16_t, uint16_t); 8612e36acbSWarner Losh static void bwi_tbl_write_4(struct bwi_mac *mac, uint16_t, uint32_t); 8712e36acbSWarner Losh 8812e36acbSWarner Losh #define SUP_BPHY(num) { .rev = num, .init = bwi_phy_init_11b_rev##num } 8912e36acbSWarner Losh 9012e36acbSWarner Losh static const struct { 9112e36acbSWarner Losh uint8_t rev; 9212e36acbSWarner Losh void (*init)(struct bwi_mac *); 9312e36acbSWarner Losh } bwi_sup_bphy[] = { 9412e36acbSWarner Losh SUP_BPHY(2), 9512e36acbSWarner Losh SUP_BPHY(4), 9612e36acbSWarner Losh SUP_BPHY(5), 9712e36acbSWarner Losh SUP_BPHY(6) 9812e36acbSWarner Losh }; 9912e36acbSWarner Losh 10012e36acbSWarner Losh #undef SUP_BPHY 10112e36acbSWarner Losh 10212e36acbSWarner Losh #define BWI_PHYTBL_WRSSI 0x1000 10312e36acbSWarner Losh #define BWI_PHYTBL_NOISE_SCALE 0x1400 10412e36acbSWarner Losh #define BWI_PHYTBL_NOISE 0x1800 10512e36acbSWarner Losh #define BWI_PHYTBL_ROTOR 0x2000 10612e36acbSWarner Losh #define BWI_PHYTBL_DELAY 0x2400 10712e36acbSWarner Losh #define BWI_PHYTBL_RSSI 0x4000 10812e36acbSWarner Losh #define BWI_PHYTBL_SIGMA_SQ 0x5000 10912e36acbSWarner Losh #define BWI_PHYTBL_WRSSI_REV1 0x5400 11012e36acbSWarner Losh #define BWI_PHYTBL_FREQ 0x5800 11112e36acbSWarner Losh 11212e36acbSWarner Losh static const uint16_t bwi_phy_freq_11g_rev1[] = 11312e36acbSWarner Losh { BWI_PHY_FREQ_11G_REV1 }; 11412e36acbSWarner Losh static const uint16_t bwi_phy_noise_11g_rev1[] = 11512e36acbSWarner Losh { BWI_PHY_NOISE_11G_REV1 }; 11612e36acbSWarner Losh static const uint16_t bwi_phy_noise_11g[] = 11712e36acbSWarner Losh { BWI_PHY_NOISE_11G }; 11812e36acbSWarner Losh static const uint32_t bwi_phy_rotor_11g_rev1[] = 11912e36acbSWarner Losh { BWI_PHY_ROTOR_11G_REV1 }; 12012e36acbSWarner Losh static const uint16_t bwi_phy_noise_scale_11g_rev2[] = 12112e36acbSWarner Losh { BWI_PHY_NOISE_SCALE_11G_REV2 }; 12212e36acbSWarner Losh static const uint16_t bwi_phy_noise_scale_11g_rev7[] = 12312e36acbSWarner Losh { BWI_PHY_NOISE_SCALE_11G_REV7 }; 12412e36acbSWarner Losh static const uint16_t bwi_phy_noise_scale_11g[] = 12512e36acbSWarner Losh { BWI_PHY_NOISE_SCALE_11G }; 12612e36acbSWarner Losh static const uint16_t bwi_phy_sigma_sq_11g_rev2[] = 12712e36acbSWarner Losh { BWI_PHY_SIGMA_SQ_11G_REV2 }; 12812e36acbSWarner Losh static const uint16_t bwi_phy_sigma_sq_11g_rev7[] = 12912e36acbSWarner Losh { BWI_PHY_SIGMA_SQ_11G_REV7 }; 13012e36acbSWarner Losh static const uint32_t bwi_phy_delay_11g_rev1[] = 13112e36acbSWarner Losh { BWI_PHY_DELAY_11G_REV1 }; 13212e36acbSWarner Losh 13312e36acbSWarner Losh void 13412e36acbSWarner Losh bwi_phy_write(struct bwi_mac *mac, uint16_t ctrl, uint16_t data) 13512e36acbSWarner Losh { 13612e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 13712e36acbSWarner Losh 13812e36acbSWarner Losh /* TODO: 11A */ 13912e36acbSWarner Losh CSR_WRITE_2(sc, BWI_PHY_CTRL, ctrl); 14012e36acbSWarner Losh CSR_WRITE_2(sc, BWI_PHY_DATA, data); 14112e36acbSWarner Losh } 14212e36acbSWarner Losh 14312e36acbSWarner Losh uint16_t 14412e36acbSWarner Losh bwi_phy_read(struct bwi_mac *mac, uint16_t ctrl) 14512e36acbSWarner Losh { 14612e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 14712e36acbSWarner Losh 14812e36acbSWarner Losh /* TODO: 11A */ 14912e36acbSWarner Losh CSR_WRITE_2(sc, BWI_PHY_CTRL, ctrl); 15012e36acbSWarner Losh return CSR_READ_2(sc, BWI_PHY_DATA); 15112e36acbSWarner Losh } 15212e36acbSWarner Losh 15312e36acbSWarner Losh int 15412e36acbSWarner Losh bwi_phy_attach(struct bwi_mac *mac) 15512e36acbSWarner Losh { 15612e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 15712e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 15812e36acbSWarner Losh uint8_t phyrev, phytype, phyver; 15912e36acbSWarner Losh uint16_t val; 16012e36acbSWarner Losh int i; 16112e36acbSWarner Losh 16212e36acbSWarner Losh /* Get PHY type/revision/version */ 16312e36acbSWarner Losh val = CSR_READ_2(sc, BWI_PHYINFO); 16412e36acbSWarner Losh phyrev = __SHIFTOUT(val, BWI_PHYINFO_REV_MASK); 16512e36acbSWarner Losh phytype = __SHIFTOUT(val, BWI_PHYINFO_TYPE_MASK); 16612e36acbSWarner Losh phyver = __SHIFTOUT(val, BWI_PHYINFO_VER_MASK); 16712e36acbSWarner Losh device_printf(sc->sc_dev, "PHY: type %d, rev %d, ver %d\n", 16812e36acbSWarner Losh phytype, phyrev, phyver); 16912e36acbSWarner Losh 17012e36acbSWarner Losh /* 17112e36acbSWarner Losh * Verify whether the revision of the PHY type is supported 17212e36acbSWarner Losh * Convert PHY type to ieee80211_phymode 17312e36acbSWarner Losh */ 17412e36acbSWarner Losh switch (phytype) { 17512e36acbSWarner Losh case BWI_PHYINFO_TYPE_11A: 17612e36acbSWarner Losh if (phyrev >= 4) { 17712e36acbSWarner Losh device_printf(sc->sc_dev, "unsupported 11A PHY, " 17812e36acbSWarner Losh "rev %u\n", phyrev); 17912e36acbSWarner Losh return ENXIO; 18012e36acbSWarner Losh } 18112e36acbSWarner Losh phy->phy_init = bwi_phy_init_11a; 18212e36acbSWarner Losh phy->phy_mode = IEEE80211_MODE_11A; 18312e36acbSWarner Losh phy->phy_tbl_ctrl = BWI_PHYR_TBL_CTRL_11A; 18412e36acbSWarner Losh phy->phy_tbl_data_lo = BWI_PHYR_TBL_DATA_LO_11A; 18512e36acbSWarner Losh phy->phy_tbl_data_hi = BWI_PHYR_TBL_DATA_HI_11A; 18612e36acbSWarner Losh break; 18712e36acbSWarner Losh case BWI_PHYINFO_TYPE_11B: 18812e36acbSWarner Losh #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 18912e36acbSWarner Losh for (i = 0; i < N(bwi_sup_bphy); ++i) { 19012e36acbSWarner Losh if (phyrev == bwi_sup_bphy[i].rev) { 19112e36acbSWarner Losh phy->phy_init = bwi_sup_bphy[i].init; 19212e36acbSWarner Losh break; 19312e36acbSWarner Losh } 19412e36acbSWarner Losh } 19512e36acbSWarner Losh if (i == N(bwi_sup_bphy)) { 19612e36acbSWarner Losh device_printf(sc->sc_dev, "unsupported 11B PHY, " 19712e36acbSWarner Losh "rev %u\n", phyrev); 19812e36acbSWarner Losh return ENXIO; 19912e36acbSWarner Losh } 20012e36acbSWarner Losh #undef N 20112e36acbSWarner Losh phy->phy_mode = IEEE80211_MODE_11B; 20212e36acbSWarner Losh break; 20312e36acbSWarner Losh case BWI_PHYINFO_TYPE_11G: 20412e36acbSWarner Losh if (phyrev > 8) { 20512e36acbSWarner Losh device_printf(sc->sc_dev, "unsupported 11G PHY, " 20612e36acbSWarner Losh "rev %u\n", phyrev); 20712e36acbSWarner Losh return ENXIO; 20812e36acbSWarner Losh } 20912e36acbSWarner Losh phy->phy_init = bwi_phy_init_11g; 21012e36acbSWarner Losh phy->phy_mode = IEEE80211_MODE_11G; 21112e36acbSWarner Losh phy->phy_tbl_ctrl = BWI_PHYR_TBL_CTRL_11G; 21212e36acbSWarner Losh phy->phy_tbl_data_lo = BWI_PHYR_TBL_DATA_LO_11G; 21312e36acbSWarner Losh phy->phy_tbl_data_hi = BWI_PHYR_TBL_DATA_HI_11G; 21412e36acbSWarner Losh break; 21512e36acbSWarner Losh default: 21612e36acbSWarner Losh device_printf(sc->sc_dev, "unsupported PHY type %d\n", 21712e36acbSWarner Losh phytype); 21812e36acbSWarner Losh return ENXIO; 21912e36acbSWarner Losh } 22012e36acbSWarner Losh phy->phy_rev = phyrev; 22112e36acbSWarner Losh phy->phy_version = phyver; 22212e36acbSWarner Losh return 0; 22312e36acbSWarner Losh } 22412e36acbSWarner Losh 22512e36acbSWarner Losh void 22612e36acbSWarner Losh bwi_phy_set_bbp_atten(struct bwi_mac *mac, uint16_t bbp_atten) 22712e36acbSWarner Losh { 22812e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 22912e36acbSWarner Losh uint16_t mask = __BITS(3, 0); 23012e36acbSWarner Losh 23112e36acbSWarner Losh if (phy->phy_version == 0) { 23212e36acbSWarner Losh CSR_FILT_SETBITS_2(mac->mac_sc, BWI_BBP_ATTEN, ~mask, 23312e36acbSWarner Losh __SHIFTIN(bbp_atten, mask)); 23412e36acbSWarner Losh } else { 23512e36acbSWarner Losh if (phy->phy_version > 1) 23612e36acbSWarner Losh mask <<= 2; 23712e36acbSWarner Losh else 23812e36acbSWarner Losh mask <<= 3; 23912e36acbSWarner Losh PHY_FILT_SETBITS(mac, BWI_PHYR_BBP_ATTEN, ~mask, 24012e36acbSWarner Losh __SHIFTIN(bbp_atten, mask)); 24112e36acbSWarner Losh } 24212e36acbSWarner Losh } 24312e36acbSWarner Losh 24412e36acbSWarner Losh int 24512e36acbSWarner Losh bwi_phy_calibrate(struct bwi_mac *mac) 24612e36acbSWarner Losh { 24712e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 24812e36acbSWarner Losh 24912e36acbSWarner Losh /* Dummy read */ 25012e36acbSWarner Losh CSR_READ_4(mac->mac_sc, BWI_MAC_STATUS); 25112e36acbSWarner Losh 25212e36acbSWarner Losh /* Don't re-init */ 25312e36acbSWarner Losh if (phy->phy_flags & BWI_PHY_F_CALIBRATED) 25412e36acbSWarner Losh return 0; 25512e36acbSWarner Losh 25612e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11G && phy->phy_rev == 1) { 25712e36acbSWarner Losh bwi_mac_reset(mac, 0); 25812e36acbSWarner Losh bwi_phy_init_11g(mac); 25912e36acbSWarner Losh bwi_mac_reset(mac, 1); 26012e36acbSWarner Losh } 26112e36acbSWarner Losh 26212e36acbSWarner Losh phy->phy_flags |= BWI_PHY_F_CALIBRATED; 26312e36acbSWarner Losh return 0; 26412e36acbSWarner Losh } 26512e36acbSWarner Losh 26612e36acbSWarner Losh static void 26712e36acbSWarner Losh bwi_tbl_write_2(struct bwi_mac *mac, uint16_t ofs, uint16_t data) 26812e36acbSWarner Losh { 26912e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 27012e36acbSWarner Losh 27112e36acbSWarner Losh KASSERT(phy->phy_tbl_ctrl != 0 && phy->phy_tbl_data_lo != 0, 27212e36acbSWarner Losh ("phy_tbl_ctrl %d phy_tbl_data_lo %d", 27312e36acbSWarner Losh phy->phy_tbl_ctrl, phy->phy_tbl_data_lo)); 27412e36acbSWarner Losh PHY_WRITE(mac, phy->phy_tbl_ctrl, ofs); 27512e36acbSWarner Losh PHY_WRITE(mac, phy->phy_tbl_data_lo, data); 27612e36acbSWarner Losh } 27712e36acbSWarner Losh 27812e36acbSWarner Losh static void 27912e36acbSWarner Losh bwi_tbl_write_4(struct bwi_mac *mac, uint16_t ofs, uint32_t data) 28012e36acbSWarner Losh { 28112e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 28212e36acbSWarner Losh 28312e36acbSWarner Losh KASSERT(phy->phy_tbl_data_lo != 0 && phy->phy_tbl_data_hi != 0 && 28412e36acbSWarner Losh phy->phy_tbl_ctrl != 0, 28512e36acbSWarner Losh ("phy_tbl_data_lo %d phy_tbl_data_hi %d phy_tbl_ctrl %d", 28612e36acbSWarner Losh phy->phy_tbl_data_lo, phy->phy_tbl_data_hi, phy->phy_tbl_ctrl)); 28712e36acbSWarner Losh 28812e36acbSWarner Losh PHY_WRITE(mac, phy->phy_tbl_ctrl, ofs); 28912e36acbSWarner Losh PHY_WRITE(mac, phy->phy_tbl_data_hi, data >> 16); 29012e36acbSWarner Losh PHY_WRITE(mac, phy->phy_tbl_data_lo, data & 0xffff); 29112e36acbSWarner Losh } 29212e36acbSWarner Losh 29312e36acbSWarner Losh void 29412e36acbSWarner Losh bwi_nrssi_write(struct bwi_mac *mac, uint16_t ofs, int16_t data) 29512e36acbSWarner Losh { 29612e36acbSWarner Losh PHY_WRITE(mac, BWI_PHYR_NRSSI_CTRL, ofs); 29712e36acbSWarner Losh PHY_WRITE(mac, BWI_PHYR_NRSSI_DATA, (uint16_t)data); 29812e36acbSWarner Losh } 29912e36acbSWarner Losh 30012e36acbSWarner Losh int16_t 30112e36acbSWarner Losh bwi_nrssi_read(struct bwi_mac *mac, uint16_t ofs) 30212e36acbSWarner Losh { 30312e36acbSWarner Losh PHY_WRITE(mac, BWI_PHYR_NRSSI_CTRL, ofs); 30412e36acbSWarner Losh return (int16_t)PHY_READ(mac, BWI_PHYR_NRSSI_DATA); 30512e36acbSWarner Losh } 30612e36acbSWarner Losh 30712e36acbSWarner Losh static void 30812e36acbSWarner Losh bwi_phy_init_11a(struct bwi_mac *mac) 30912e36acbSWarner Losh { 31012e36acbSWarner Losh /* TODO:11A */ 31112e36acbSWarner Losh } 31212e36acbSWarner Losh 31312e36acbSWarner Losh static void 31412e36acbSWarner Losh bwi_phy_init_11g(struct bwi_mac *mac) 31512e36acbSWarner Losh { 31612e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 31712e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 31812e36acbSWarner Losh struct bwi_rf *rf = &mac->mac_rf; 31912e36acbSWarner Losh const struct bwi_tpctl *tpctl = &mac->mac_tpctl; 32012e36acbSWarner Losh 32112e36acbSWarner Losh if (phy->phy_rev == 1) 32212e36acbSWarner Losh bwi_phy_init_11b_rev5(mac); 32312e36acbSWarner Losh else 32412e36acbSWarner Losh bwi_phy_init_11b_rev6(mac); 32512e36acbSWarner Losh 32612e36acbSWarner Losh if (phy->phy_rev >= 2 || (phy->phy_flags & BWI_PHY_F_LINKED)) 32712e36acbSWarner Losh bwi_phy_config_11g(mac); 32812e36acbSWarner Losh 32912e36acbSWarner Losh if (phy->phy_rev >= 2) { 33012e36acbSWarner Losh PHY_WRITE(mac, 0x814, 0); 33112e36acbSWarner Losh PHY_WRITE(mac, 0x815, 0); 33212e36acbSWarner Losh 33312e36acbSWarner Losh if (phy->phy_rev == 2) { 33412e36acbSWarner Losh PHY_WRITE(mac, 0x811, 0); 33512e36acbSWarner Losh PHY_WRITE(mac, 0x15, 0xc0); 33612e36acbSWarner Losh } else if (phy->phy_rev > 5) { 33712e36acbSWarner Losh PHY_WRITE(mac, 0x811, 0x400); 33812e36acbSWarner Losh PHY_WRITE(mac, 0x15, 0xc0); 33912e36acbSWarner Losh } 34012e36acbSWarner Losh } 34112e36acbSWarner Losh 34212e36acbSWarner Losh if (phy->phy_rev >= 2 || (phy->phy_flags & BWI_PHY_F_LINKED)) { 34312e36acbSWarner Losh uint16_t val; 34412e36acbSWarner Losh 34512e36acbSWarner Losh val = PHY_READ(mac, 0x400) & 0xff; 34612e36acbSWarner Losh if (val == 3 || val == 5) { 34712e36acbSWarner Losh PHY_WRITE(mac, 0x4c2, 0x1816); 34812e36acbSWarner Losh PHY_WRITE(mac, 0x4c3, 0x8006); 34912e36acbSWarner Losh if (val == 5) { 35012e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4cc, 35112e36acbSWarner Losh 0xff, 0x1f00); 35212e36acbSWarner Losh } 35312e36acbSWarner Losh } 35412e36acbSWarner Losh } 35512e36acbSWarner Losh 35612e36acbSWarner Losh if ((phy->phy_rev <= 2 && (phy->phy_flags & BWI_PHY_F_LINKED)) || 35712e36acbSWarner Losh phy->phy_rev >= 2) 35812e36acbSWarner Losh PHY_WRITE(mac, 0x47e, 0x78); 35912e36acbSWarner Losh 36012e36acbSWarner Losh if (rf->rf_rev == 8) { 36112e36acbSWarner Losh PHY_SETBITS(mac, 0x801, 0x80); 36212e36acbSWarner Losh PHY_SETBITS(mac, 0x43e, 0x4); 36312e36acbSWarner Losh } 36412e36acbSWarner Losh 36512e36acbSWarner Losh if (phy->phy_rev >= 2 && (phy->phy_flags & BWI_PHY_F_LINKED)) 36612e36acbSWarner Losh bwi_rf_get_gains(mac); 36712e36acbSWarner Losh 36812e36acbSWarner Losh if (rf->rf_rev != 8) 36912e36acbSWarner Losh bwi_rf_init(mac); 37012e36acbSWarner Losh 37112e36acbSWarner Losh if (tpctl->tp_ctrl2 == 0xffff) { 37212e36acbSWarner Losh bwi_rf_lo_update(mac); 37312e36acbSWarner Losh } else { 37412e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev == 8) { 37512e36acbSWarner Losh RF_WRITE(mac, 0x52, 37612e36acbSWarner Losh (tpctl->tp_ctrl1 << 4) | tpctl->tp_ctrl2); 37712e36acbSWarner Losh } else { 37812e36acbSWarner Losh RF_FILT_SETBITS(mac, 0x52, 0xfff0, tpctl->tp_ctrl1); 37912e36acbSWarner Losh } 38012e36acbSWarner Losh 38112e36acbSWarner Losh if (phy->phy_rev >= 6) { 38212e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x36, 0xfff, 38312e36acbSWarner Losh tpctl->tp_ctrl2 << 12); 38412e36acbSWarner Losh } 38512e36acbSWarner Losh 38612e36acbSWarner Losh if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) 38712e36acbSWarner Losh PHY_WRITE(mac, 0x2e, 0x8075); 38812e36acbSWarner Losh else 38912e36acbSWarner Losh PHY_WRITE(mac, 0x2e, 0x807f); 39012e36acbSWarner Losh 39112e36acbSWarner Losh if (phy->phy_rev < 2) 39212e36acbSWarner Losh PHY_WRITE(mac, 0x2f, 0x101); 39312e36acbSWarner Losh else 39412e36acbSWarner Losh PHY_WRITE(mac, 0x2f, 0x202); 39512e36acbSWarner Losh } 39612e36acbSWarner Losh 39712e36acbSWarner Losh if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) { 39812e36acbSWarner Losh bwi_rf_lo_adjust(mac, tpctl); 39912e36acbSWarner Losh PHY_WRITE(mac, 0x80f, 0x8078); 40012e36acbSWarner Losh } 40112e36acbSWarner Losh 40212e36acbSWarner Losh if ((sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) == 0) { 40312e36acbSWarner Losh bwi_rf_init_hw_nrssi_table(mac, 0xffff /* XXX */); 40412e36acbSWarner Losh bwi_rf_set_nrssi_thr(mac); 40512e36acbSWarner Losh } else if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) { 40612e36acbSWarner Losh if (rf->rf_nrssi[0] == BWI_INVALID_NRSSI) { 40712e36acbSWarner Losh KASSERT(rf->rf_nrssi[1] == BWI_INVALID_NRSSI, 40812e36acbSWarner Losh ("rf_nrssi[1] %d", rf->rf_nrssi[1])); 40912e36acbSWarner Losh bwi_rf_calc_nrssi_slope(mac); 41012e36acbSWarner Losh } else { 41112e36acbSWarner Losh KASSERT(rf->rf_nrssi[1] != BWI_INVALID_NRSSI, 41212e36acbSWarner Losh ("rf_nrssi[1] %d", rf->rf_nrssi[1])); 41312e36acbSWarner Losh bwi_rf_set_nrssi_thr(mac); 41412e36acbSWarner Losh } 41512e36acbSWarner Losh } 41612e36acbSWarner Losh 41712e36acbSWarner Losh if (rf->rf_rev == 8) 41812e36acbSWarner Losh PHY_WRITE(mac, 0x805, 0x3230); 41912e36acbSWarner Losh 42012e36acbSWarner Losh bwi_mac_init_tpctl_11bg(mac); 42112e36acbSWarner Losh 42212e36acbSWarner Losh if (sc->sc_bbp_id == BWI_BBPID_BCM4306 && sc->sc_bbp_pkg == 2) { 42312e36acbSWarner Losh PHY_CLRBITS(mac, 0x429, 0x4000); 42412e36acbSWarner Losh PHY_CLRBITS(mac, 0x4c3, 0x8000); 42512e36acbSWarner Losh } 42612e36acbSWarner Losh } 42712e36acbSWarner Losh 42812e36acbSWarner Losh static void 42912e36acbSWarner Losh bwi_phy_init_11b_rev2(struct bwi_mac *mac) 43012e36acbSWarner Losh { 43112e36acbSWarner Losh /* TODO:11B */ 43212e36acbSWarner Losh if_printf(mac->mac_sc->sc_ifp, 43312e36acbSWarner Losh "%s is not implemented yet\n", __func__); 43412e36acbSWarner Losh } 43512e36acbSWarner Losh 43612e36acbSWarner Losh static void 43712e36acbSWarner Losh bwi_phy_init_11b_rev4(struct bwi_mac *mac) 43812e36acbSWarner Losh { 43912e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 44012e36acbSWarner Losh struct bwi_rf *rf = &mac->mac_rf; 44112e36acbSWarner Losh uint16_t val, ofs; 44212e36acbSWarner Losh u_int chan; 44312e36acbSWarner Losh 44412e36acbSWarner Losh CSR_WRITE_2(sc, BWI_BPHY_CTRL, BWI_BPHY_CTRL_INIT); 44512e36acbSWarner Losh 44612e36acbSWarner Losh PHY_WRITE(mac, 0x20, 0x301c); 44712e36acbSWarner Losh PHY_WRITE(mac, 0x26, 0); 44812e36acbSWarner Losh PHY_WRITE(mac, 0x30, 0xc6); 44912e36acbSWarner Losh PHY_WRITE(mac, 0x88, 0x3e00); 45012e36acbSWarner Losh 45112e36acbSWarner Losh for (ofs = 0, val = 0x3c3d; ofs < 30; ++ofs, val -= 0x202) 45212e36acbSWarner Losh PHY_WRITE(mac, 0x89 + ofs, val); 45312e36acbSWarner Losh 45412e36acbSWarner Losh CSR_WRITE_2(sc, BWI_PHY_MAGIC_REG1, BWI_PHY_MAGIC_REG1_VAL1); 45512e36acbSWarner Losh 45612e36acbSWarner Losh chan = rf->rf_curchan; 45712e36acbSWarner Losh if (chan == IEEE80211_CHAN_ANY) 45812e36acbSWarner Losh chan = 6; /* Force to channel 6 */ 45912e36acbSWarner Losh bwi_rf_set_chan(mac, chan, 0); 46012e36acbSWarner Losh 46112e36acbSWarner Losh if (rf->rf_type != BWI_RF_T_BCM2050) { 46212e36acbSWarner Losh RF_WRITE(mac, 0x75, 0x80); 46312e36acbSWarner Losh RF_WRITE(mac, 0x79, 0x81); 46412e36acbSWarner Losh } 46512e36acbSWarner Losh 46612e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x20); 46712e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x23); 46812e36acbSWarner Losh 46912e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) { 47012e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x20); 47112e36acbSWarner Losh RF_WRITE(mac, 0x5a, 0x70); 47212e36acbSWarner Losh RF_WRITE(mac, 0x5b, 0x7b); 47312e36acbSWarner Losh RF_WRITE(mac, 0x5c, 0xb0); 47412e36acbSWarner Losh RF_WRITE(mac, 0x7a, 0xf); 47512e36acbSWarner Losh PHY_WRITE(mac, 0x38, 0x677); 47612e36acbSWarner Losh bwi_rf_init_bcm2050(mac); 47712e36acbSWarner Losh } 47812e36acbSWarner Losh 47912e36acbSWarner Losh PHY_WRITE(mac, 0x14, 0x80); 48012e36acbSWarner Losh PHY_WRITE(mac, 0x32, 0xca); 48112e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) 48212e36acbSWarner Losh PHY_WRITE(mac, 0x32, 0xe0); 48312e36acbSWarner Losh PHY_WRITE(mac, 0x35, 0x7c2); 48412e36acbSWarner Losh 48512e36acbSWarner Losh bwi_rf_lo_update(mac); 48612e36acbSWarner Losh 48712e36acbSWarner Losh PHY_WRITE(mac, 0x26, 0xcc00); 48812e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) 48912e36acbSWarner Losh PHY_WRITE(mac, 0x26, 0xce00); 49012e36acbSWarner Losh 49112e36acbSWarner Losh CSR_WRITE_2(sc, BWI_RF_CHAN_EX, 0x1100); 49212e36acbSWarner Losh 49312e36acbSWarner Losh PHY_WRITE(mac, 0x2a, 0x88a3); 49412e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) 49512e36acbSWarner Losh PHY_WRITE(mac, 0x2a, 0x88c2); 49612e36acbSWarner Losh 49712e36acbSWarner Losh bwi_mac_set_tpctl_11bg(mac, NULL); 49812e36acbSWarner Losh if (sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) { 49912e36acbSWarner Losh bwi_rf_calc_nrssi_slope(mac); 50012e36acbSWarner Losh bwi_rf_set_nrssi_thr(mac); 50112e36acbSWarner Losh } 50212e36acbSWarner Losh bwi_mac_init_tpctl_11bg(mac); 50312e36acbSWarner Losh } 50412e36acbSWarner Losh 50512e36acbSWarner Losh static void 50612e36acbSWarner Losh bwi_phy_init_11b_rev5(struct bwi_mac *mac) 50712e36acbSWarner Losh { 50812e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 50912e36acbSWarner Losh struct bwi_rf *rf = &mac->mac_rf; 51012e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 51112e36acbSWarner Losh u_int orig_chan; 51212e36acbSWarner Losh 51312e36acbSWarner Losh if (phy->phy_version == 1) 51412e36acbSWarner Losh RF_SETBITS(mac, 0x7a, 0x50); 51512e36acbSWarner Losh 51612e36acbSWarner Losh if (sc->sc_pci_subvid != PCI_VENDOR_BROADCOM && 51712e36acbSWarner Losh sc->sc_pci_subdid != BWI_PCI_SUBDEVICE_BU4306) { 51812e36acbSWarner Losh uint16_t ofs, val; 51912e36acbSWarner Losh 52012e36acbSWarner Losh val = 0x2120; 52112e36acbSWarner Losh for (ofs = 0xa8; ofs < 0xc7; ++ofs) { 52212e36acbSWarner Losh PHY_WRITE(mac, ofs, val); 52312e36acbSWarner Losh val += 0x202; 52412e36acbSWarner Losh } 52512e36acbSWarner Losh } 52612e36acbSWarner Losh 52712e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x35, 0xf0ff, 0x700); 52812e36acbSWarner Losh 52912e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) 53012e36acbSWarner Losh PHY_WRITE(mac, 0x38, 0x667); 53112e36acbSWarner Losh 53212e36acbSWarner Losh if ((phy->phy_flags & BWI_PHY_F_LINKED) || phy->phy_rev >= 2) { 53312e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) { 53412e36acbSWarner Losh RF_SETBITS(mac, 0x7a, 0x20); 53512e36acbSWarner Losh RF_SETBITS(mac, 0x51, 0x4); 53612e36acbSWarner Losh } 53712e36acbSWarner Losh 53812e36acbSWarner Losh CSR_WRITE_2(sc, BWI_RF_ANTDIV, 0); 53912e36acbSWarner Losh 54012e36acbSWarner Losh PHY_SETBITS(mac, 0x802, 0x100); 54112e36acbSWarner Losh PHY_SETBITS(mac, 0x42b, 0x2000); 54212e36acbSWarner Losh PHY_WRITE(mac, 0x1c, 0x186a); 54312e36acbSWarner Losh 54412e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x13, 0xff, 0x1900); 54512e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x35, 0xffc0, 0x64); 54612e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x5d, 0xff80, 0xa); 54712e36acbSWarner Losh } 54812e36acbSWarner Losh 54912e36acbSWarner Losh /* TODO: bad_frame_preempt? */ 55012e36acbSWarner Losh 55112e36acbSWarner Losh if (phy->phy_version == 1) { 55212e36acbSWarner Losh PHY_WRITE(mac, 0x26, 0xce00); 55312e36acbSWarner Losh PHY_WRITE(mac, 0x21, 0x3763); 55412e36acbSWarner Losh PHY_WRITE(mac, 0x22, 0x1bc3); 55512e36acbSWarner Losh PHY_WRITE(mac, 0x23, 0x6f9); 55612e36acbSWarner Losh PHY_WRITE(mac, 0x24, 0x37e); 55712e36acbSWarner Losh } else { 55812e36acbSWarner Losh PHY_WRITE(mac, 0x26, 0xcc00); 55912e36acbSWarner Losh } 56012e36acbSWarner Losh PHY_WRITE(mac, 0x30, 0xc6); 56112e36acbSWarner Losh 56212e36acbSWarner Losh CSR_WRITE_2(sc, BWI_BPHY_CTRL, BWI_BPHY_CTRL_INIT); 56312e36acbSWarner Losh 56412e36acbSWarner Losh if (phy->phy_version == 1) 56512e36acbSWarner Losh PHY_WRITE(mac, 0x20, 0x3e1c); 56612e36acbSWarner Losh else 56712e36acbSWarner Losh PHY_WRITE(mac, 0x20, 0x301c); 56812e36acbSWarner Losh 56912e36acbSWarner Losh if (phy->phy_version == 0) 57012e36acbSWarner Losh CSR_WRITE_2(sc, BWI_PHY_MAGIC_REG1, BWI_PHY_MAGIC_REG1_VAL1); 57112e36acbSWarner Losh 57212e36acbSWarner Losh /* Force to channel 7 */ 57312e36acbSWarner Losh orig_chan = rf->rf_curchan; 57412e36acbSWarner Losh bwi_rf_set_chan(mac, 7, 0); 57512e36acbSWarner Losh 57612e36acbSWarner Losh if (rf->rf_type != BWI_RF_T_BCM2050) { 57712e36acbSWarner Losh RF_WRITE(mac, 0x75, 0x80); 57812e36acbSWarner Losh RF_WRITE(mac, 0x79, 0x81); 57912e36acbSWarner Losh } 58012e36acbSWarner Losh 58112e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x20); 58212e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x23); 58312e36acbSWarner Losh 58412e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) { 58512e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x20); 58612e36acbSWarner Losh RF_WRITE(mac, 0x5a, 0x70); 58712e36acbSWarner Losh } 58812e36acbSWarner Losh 58912e36acbSWarner Losh RF_WRITE(mac, 0x5b, 0x7b); 59012e36acbSWarner Losh RF_WRITE(mac, 0x5c, 0xb0); 59112e36acbSWarner Losh RF_SETBITS(mac, 0x7a, 0x7); 59212e36acbSWarner Losh 59312e36acbSWarner Losh bwi_rf_set_chan(mac, orig_chan, 0); 59412e36acbSWarner Losh 59512e36acbSWarner Losh PHY_WRITE(mac, 0x14, 0x80); 59612e36acbSWarner Losh PHY_WRITE(mac, 0x32, 0xca); 59712e36acbSWarner Losh PHY_WRITE(mac, 0x2a, 0x88a3); 59812e36acbSWarner Losh 59912e36acbSWarner Losh bwi_mac_set_tpctl_11bg(mac, NULL); 60012e36acbSWarner Losh 60112e36acbSWarner Losh if (rf->rf_type == BWI_RF_T_BCM2050) 60212e36acbSWarner Losh RF_WRITE(mac, 0x5d, 0xd); 60312e36acbSWarner Losh 60412e36acbSWarner Losh CSR_FILT_SETBITS_2(sc, BWI_PHY_MAGIC_REG1, 0xffc0, 0x4); 60512e36acbSWarner Losh } 60612e36acbSWarner Losh 60712e36acbSWarner Losh static void 60812e36acbSWarner Losh bwi_phy_init_11b_rev6(struct bwi_mac *mac) 60912e36acbSWarner Losh { 61012e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 61112e36acbSWarner Losh struct bwi_rf *rf = &mac->mac_rf; 61212e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 61312e36acbSWarner Losh uint16_t val, ofs; 61412e36acbSWarner Losh u_int orig_chan; 61512e36acbSWarner Losh 61612e36acbSWarner Losh PHY_WRITE(mac, 0x3e, 0x817a); 61712e36acbSWarner Losh RF_SETBITS(mac, 0x7a, 0x58); 61812e36acbSWarner Losh 61912e36acbSWarner Losh if (rf->rf_rev == 4 || rf->rf_rev == 5) { 62012e36acbSWarner Losh RF_WRITE(mac, 0x51, 0x37); 62112e36acbSWarner Losh RF_WRITE(mac, 0x52, 0x70); 62212e36acbSWarner Losh RF_WRITE(mac, 0x53, 0xb3); 62312e36acbSWarner Losh RF_WRITE(mac, 0x54, 0x9b); 62412e36acbSWarner Losh RF_WRITE(mac, 0x5a, 0x88); 62512e36acbSWarner Losh RF_WRITE(mac, 0x5b, 0x88); 62612e36acbSWarner Losh RF_WRITE(mac, 0x5d, 0x88); 62712e36acbSWarner Losh RF_WRITE(mac, 0x5e, 0x88); 62812e36acbSWarner Losh RF_WRITE(mac, 0x7d, 0x88); 62912e36acbSWarner Losh HFLAGS_SETBITS(mac, BWI_HFLAG_MAGIC1); 63012e36acbSWarner Losh } else if (rf->rf_rev == 8) { 63112e36acbSWarner Losh RF_WRITE(mac, 0x51, 0); 63212e36acbSWarner Losh RF_WRITE(mac, 0x52, 0x40); 63312e36acbSWarner Losh RF_WRITE(mac, 0x53, 0xb7); 63412e36acbSWarner Losh RF_WRITE(mac, 0x54, 0x98); 63512e36acbSWarner Losh RF_WRITE(mac, 0x5a, 0x88); 63612e36acbSWarner Losh RF_WRITE(mac, 0x5b, 0x6b); 63712e36acbSWarner Losh RF_WRITE(mac, 0x5c, 0xf); 63812e36acbSWarner Losh if (sc->sc_card_flags & BWI_CARD_F_ALT_IQ) { 63912e36acbSWarner Losh RF_WRITE(mac, 0x5d, 0xfa); 64012e36acbSWarner Losh RF_WRITE(mac, 0x5e, 0xd8); 64112e36acbSWarner Losh } else { 64212e36acbSWarner Losh RF_WRITE(mac, 0x5d, 0xf5); 64312e36acbSWarner Losh RF_WRITE(mac, 0x5e, 0xb8); 64412e36acbSWarner Losh } 64512e36acbSWarner Losh RF_WRITE(mac, 0x73, 0x3); 64612e36acbSWarner Losh RF_WRITE(mac, 0x7d, 0xa8); 64712e36acbSWarner Losh RF_WRITE(mac, 0x7c, 0x1); 64812e36acbSWarner Losh RF_WRITE(mac, 0x7e, 0x8); 64912e36acbSWarner Losh } 65012e36acbSWarner Losh 65112e36acbSWarner Losh val = 0x1e1f; 65212e36acbSWarner Losh for (ofs = 0x88; ofs < 0x98; ++ofs) { 65312e36acbSWarner Losh PHY_WRITE(mac, ofs, val); 65412e36acbSWarner Losh val -= 0x202; 65512e36acbSWarner Losh } 65612e36acbSWarner Losh 65712e36acbSWarner Losh val = 0x3e3f; 65812e36acbSWarner Losh for (ofs = 0x98; ofs < 0xa8; ++ofs) { 65912e36acbSWarner Losh PHY_WRITE(mac, ofs, val); 66012e36acbSWarner Losh val -= 0x202; 66112e36acbSWarner Losh } 66212e36acbSWarner Losh 66312e36acbSWarner Losh val = 0x2120; 66412e36acbSWarner Losh for (ofs = 0xa8; ofs < 0xc8; ++ofs) { 66512e36acbSWarner Losh PHY_WRITE(mac, ofs, (val & 0x3f3f)); 66612e36acbSWarner Losh val += 0x202; 66712e36acbSWarner Losh } 66812e36acbSWarner Losh 66912e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11G) { 67012e36acbSWarner Losh RF_SETBITS(mac, 0x7a, 0x20); 67112e36acbSWarner Losh RF_SETBITS(mac, 0x51, 0x4); 67212e36acbSWarner Losh PHY_SETBITS(mac, 0x802, 0x100); 67312e36acbSWarner Losh PHY_SETBITS(mac, 0x42b, 0x2000); 67412e36acbSWarner Losh PHY_WRITE(mac, 0x5b, 0); 67512e36acbSWarner Losh PHY_WRITE(mac, 0x5c, 0); 67612e36acbSWarner Losh } 67712e36acbSWarner Losh 67812e36acbSWarner Losh /* Force to channel 7 */ 67912e36acbSWarner Losh orig_chan = rf->rf_curchan; 68012e36acbSWarner Losh if (orig_chan >= 8) 68112e36acbSWarner Losh bwi_rf_set_chan(mac, 1, 0); 68212e36acbSWarner Losh else 68312e36acbSWarner Losh bwi_rf_set_chan(mac, 13, 0); 68412e36acbSWarner Losh 68512e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x20); 68612e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x23); 68712e36acbSWarner Losh 68812e36acbSWarner Losh DELAY(40); 68912e36acbSWarner Losh 69012e36acbSWarner Losh if (rf->rf_rev < 6 || rf->rf_rev == 8) { 69112e36acbSWarner Losh RF_SETBITS(mac, 0x7c, 0x2); 69212e36acbSWarner Losh RF_WRITE(mac, 0x50, 0x20); 69312e36acbSWarner Losh } 69412e36acbSWarner Losh if (rf->rf_rev <= 2) { 69512e36acbSWarner Losh RF_WRITE(mac, 0x7c, 0x20); 69612e36acbSWarner Losh RF_WRITE(mac, 0x5a, 0x70); 69712e36acbSWarner Losh RF_WRITE(mac, 0x5b, 0x7b); 69812e36acbSWarner Losh RF_WRITE(mac, 0x5c, 0xb0); 69912e36acbSWarner Losh } 70012e36acbSWarner Losh 70112e36acbSWarner Losh RF_FILT_SETBITS(mac, 0x7a, 0xf8, 0x7); 70212e36acbSWarner Losh 70312e36acbSWarner Losh bwi_rf_set_chan(mac, orig_chan, 0); 70412e36acbSWarner Losh 70512e36acbSWarner Losh PHY_WRITE(mac, 0x14, 0x200); 70612e36acbSWarner Losh if (rf->rf_rev >= 6) 70712e36acbSWarner Losh PHY_WRITE(mac, 0x2a, 0x88c2); 70812e36acbSWarner Losh else 70912e36acbSWarner Losh PHY_WRITE(mac, 0x2a, 0x8ac0); 71012e36acbSWarner Losh PHY_WRITE(mac, 0x38, 0x668); 71112e36acbSWarner Losh 71212e36acbSWarner Losh bwi_mac_set_tpctl_11bg(mac, NULL); 71312e36acbSWarner Losh 71412e36acbSWarner Losh if (rf->rf_rev <= 5) { 71512e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x5d, 0xff80, 0x3); 71612e36acbSWarner Losh if (rf->rf_rev <= 2) 71712e36acbSWarner Losh RF_WRITE(mac, 0x5d, 0xd); 71812e36acbSWarner Losh } 71912e36acbSWarner Losh 72012e36acbSWarner Losh if (phy->phy_version == 4) { 72112e36acbSWarner Losh CSR_WRITE_2(sc, BWI_PHY_MAGIC_REG1, BWI_PHY_MAGIC_REG1_VAL2); 72212e36acbSWarner Losh PHY_CLRBITS(mac, 0x61, 0xf000); 72312e36acbSWarner Losh } else { 72412e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x2, 0xffc0, 0x4); 72512e36acbSWarner Losh } 72612e36acbSWarner Losh 72712e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11B) { 72812e36acbSWarner Losh CSR_WRITE_2(sc, BWI_BBP_ATTEN, BWI_BBP_ATTEN_MAGIC2); 72912e36acbSWarner Losh PHY_WRITE(mac, 0x16, 0x410); 73012e36acbSWarner Losh PHY_WRITE(mac, 0x17, 0x820); 73112e36acbSWarner Losh PHY_WRITE(mac, 0x62, 0x7); 73212e36acbSWarner Losh 73312e36acbSWarner Losh bwi_rf_init_bcm2050(mac); 73412e36acbSWarner Losh bwi_rf_lo_update(mac); 73512e36acbSWarner Losh if (sc->sc_card_flags & BWI_CARD_F_SW_NRSSI) { 73612e36acbSWarner Losh bwi_rf_calc_nrssi_slope(mac); 73712e36acbSWarner Losh bwi_rf_set_nrssi_thr(mac); 73812e36acbSWarner Losh } 73912e36acbSWarner Losh bwi_mac_init_tpctl_11bg(mac); 74012e36acbSWarner Losh } else { 74112e36acbSWarner Losh CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0); 74212e36acbSWarner Losh } 74312e36acbSWarner Losh } 74412e36acbSWarner Losh 74512e36acbSWarner Losh #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 74612e36acbSWarner Losh 74712e36acbSWarner Losh static void 74812e36acbSWarner Losh bwi_phy_config_11g(struct bwi_mac *mac) 74912e36acbSWarner Losh { 75012e36acbSWarner Losh struct bwi_softc *sc = mac->mac_sc; 75112e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 75212e36acbSWarner Losh const uint16_t *tbl; 75312e36acbSWarner Losh uint16_t wrd_ofs1, wrd_ofs2; 75412e36acbSWarner Losh int i, n; 75512e36acbSWarner Losh 75612e36acbSWarner Losh if (phy->phy_rev == 1) { 75712e36acbSWarner Losh PHY_WRITE(mac, 0x406, 0x4f19); 75812e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x429, 0xfc3f, 0x340); 75912e36acbSWarner Losh PHY_WRITE(mac, 0x42c, 0x5a); 76012e36acbSWarner Losh PHY_WRITE(mac, 0x427, 0x1a); 76112e36acbSWarner Losh 76212e36acbSWarner Losh /* Fill frequency table */ 76312e36acbSWarner Losh for (i = 0; i < N(bwi_phy_freq_11g_rev1); ++i) { 76412e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_FREQ + i, 76512e36acbSWarner Losh bwi_phy_freq_11g_rev1[i]); 76612e36acbSWarner Losh } 76712e36acbSWarner Losh 76812e36acbSWarner Losh /* Fill noise table */ 76912e36acbSWarner Losh for (i = 0; i < N(bwi_phy_noise_11g_rev1); ++i) { 77012e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_NOISE + i, 77112e36acbSWarner Losh bwi_phy_noise_11g_rev1[i]); 77212e36acbSWarner Losh } 77312e36acbSWarner Losh 77412e36acbSWarner Losh /* Fill rotor table */ 77512e36acbSWarner Losh for (i = 0; i < N(bwi_phy_rotor_11g_rev1); ++i) { 77612e36acbSWarner Losh /* NB: data length is 4 bytes */ 77712e36acbSWarner Losh bwi_tbl_write_4(mac, BWI_PHYTBL_ROTOR + i, 77812e36acbSWarner Losh bwi_phy_rotor_11g_rev1[i]); 77912e36acbSWarner Losh } 78012e36acbSWarner Losh } else { 78112e36acbSWarner Losh bwi_nrssi_write(mac, 0xba98, (int16_t)0x7654); /* XXX */ 78212e36acbSWarner Losh 78312e36acbSWarner Losh if (phy->phy_rev == 2) { 78412e36acbSWarner Losh PHY_WRITE(mac, 0x4c0, 0x1861); 78512e36acbSWarner Losh PHY_WRITE(mac, 0x4c1, 0x271); 78612e36acbSWarner Losh } else if (phy->phy_rev > 2) { 78712e36acbSWarner Losh PHY_WRITE(mac, 0x4c0, 0x98); 78812e36acbSWarner Losh PHY_WRITE(mac, 0x4c1, 0x70); 78912e36acbSWarner Losh PHY_WRITE(mac, 0x4c9, 0x80); 79012e36acbSWarner Losh } 79112e36acbSWarner Losh PHY_SETBITS(mac, 0x42b, 0x800); 79212e36acbSWarner Losh 79312e36acbSWarner Losh /* Fill RSSI table */ 79412e36acbSWarner Losh for (i = 0; i < 64; ++i) 79512e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_RSSI + i, i); 79612e36acbSWarner Losh 79712e36acbSWarner Losh /* Fill noise table */ 79812e36acbSWarner Losh for (i = 0; i < sizeof(bwi_phy_noise_11g); ++i) { 79912e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_NOISE + i, 80012e36acbSWarner Losh bwi_phy_noise_11g[i]); 80112e36acbSWarner Losh } 80212e36acbSWarner Losh } 80312e36acbSWarner Losh 80412e36acbSWarner Losh /* 80512e36acbSWarner Losh * Fill noise scale table 80612e36acbSWarner Losh */ 80712e36acbSWarner Losh if (phy->phy_rev <= 2) { 80812e36acbSWarner Losh tbl = bwi_phy_noise_scale_11g_rev2; 80912e36acbSWarner Losh n = N(bwi_phy_noise_scale_11g_rev2); 81012e36acbSWarner Losh } else if (phy->phy_rev >= 7 && (PHY_READ(mac, 0x449) & 0x200)) { 81112e36acbSWarner Losh tbl = bwi_phy_noise_scale_11g_rev7; 81212e36acbSWarner Losh n = N(bwi_phy_noise_scale_11g_rev7); 81312e36acbSWarner Losh } else { 81412e36acbSWarner Losh tbl = bwi_phy_noise_scale_11g; 81512e36acbSWarner Losh n = N(bwi_phy_noise_scale_11g); 81612e36acbSWarner Losh } 81712e36acbSWarner Losh for (i = 0; i < n; ++i) 81812e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_NOISE_SCALE + i, tbl[i]); 81912e36acbSWarner Losh 82012e36acbSWarner Losh /* 82112e36acbSWarner Losh * Fill sigma square table 82212e36acbSWarner Losh */ 82312e36acbSWarner Losh if (phy->phy_rev == 2) { 82412e36acbSWarner Losh tbl = bwi_phy_sigma_sq_11g_rev2; 82512e36acbSWarner Losh n = N(bwi_phy_sigma_sq_11g_rev2); 82612e36acbSWarner Losh } else if (phy->phy_rev > 2 && phy->phy_rev <= 8) { 82712e36acbSWarner Losh tbl = bwi_phy_sigma_sq_11g_rev7; 82812e36acbSWarner Losh n = N(bwi_phy_sigma_sq_11g_rev7); 82912e36acbSWarner Losh } else { 83012e36acbSWarner Losh tbl = NULL; 83112e36acbSWarner Losh n = 0; 83212e36acbSWarner Losh } 83312e36acbSWarner Losh for (i = 0; i < n; ++i) 83412e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_SIGMA_SQ + i, tbl[i]); 83512e36acbSWarner Losh 83612e36acbSWarner Losh if (phy->phy_rev == 1) { 83712e36acbSWarner Losh /* Fill delay table */ 83812e36acbSWarner Losh for (i = 0; i < N(bwi_phy_delay_11g_rev1); ++i) { 83912e36acbSWarner Losh bwi_tbl_write_4(mac, BWI_PHYTBL_DELAY + i, 84012e36acbSWarner Losh bwi_phy_delay_11g_rev1[i]); 84112e36acbSWarner Losh } 84212e36acbSWarner Losh 84312e36acbSWarner Losh /* Fill WRSSI (Wide-Band RSSI) table */ 84412e36acbSWarner Losh for (i = 4; i < 20; ++i) 84512e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_WRSSI_REV1 + i, 0x20); 84612e36acbSWarner Losh 84712e36acbSWarner Losh bwi_phy_config_agc(mac); 84812e36acbSWarner Losh 84912e36acbSWarner Losh wrd_ofs1 = 0x5001; 85012e36acbSWarner Losh wrd_ofs2 = 0x5002; 85112e36acbSWarner Losh } else { 85212e36acbSWarner Losh /* Fill WRSSI (Wide-Band RSSI) table */ 85312e36acbSWarner Losh for (i = 0; i < 0x20; ++i) 85412e36acbSWarner Losh bwi_tbl_write_2(mac, BWI_PHYTBL_WRSSI + i, 0x820); 85512e36acbSWarner Losh 85612e36acbSWarner Losh bwi_phy_config_agc(mac); 85712e36acbSWarner Losh 85812e36acbSWarner Losh PHY_READ(mac, 0x400); /* Dummy read */ 85912e36acbSWarner Losh PHY_WRITE(mac, 0x403, 0x1000); 86012e36acbSWarner Losh bwi_tbl_write_2(mac, 0x3c02, 0xf); 86112e36acbSWarner Losh bwi_tbl_write_2(mac, 0x3c03, 0x14); 86212e36acbSWarner Losh 86312e36acbSWarner Losh wrd_ofs1 = 0x401; 86412e36acbSWarner Losh wrd_ofs2 = 0x402; 86512e36acbSWarner Losh } 86612e36acbSWarner Losh 86712e36acbSWarner Losh if (!(BWI_IS_BRCM_BU4306(sc) && sc->sc_pci_revid == 0x17)) { 86812e36acbSWarner Losh bwi_tbl_write_2(mac, wrd_ofs1, 0x2); 86912e36acbSWarner Losh bwi_tbl_write_2(mac, wrd_ofs2, 0x1); 87012e36acbSWarner Losh } 87112e36acbSWarner Losh 87212e36acbSWarner Losh /* phy->phy_flags & BWI_PHY_F_LINKED ? */ 87312e36acbSWarner Losh if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) 87412e36acbSWarner Losh PHY_WRITE(mac, 0x46e, 0x3cf); 87512e36acbSWarner Losh } 87612e36acbSWarner Losh 87712e36acbSWarner Losh #undef N 87812e36acbSWarner Losh 87912e36acbSWarner Losh /* 88012e36acbSWarner Losh * Configure Automatic Gain Controller 88112e36acbSWarner Losh */ 88212e36acbSWarner Losh static void 88312e36acbSWarner Losh bwi_phy_config_agc(struct bwi_mac *mac) 88412e36acbSWarner Losh { 88512e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 88612e36acbSWarner Losh uint16_t ofs; 88712e36acbSWarner Losh 88812e36acbSWarner Losh ofs = phy->phy_rev == 1 ? 0x4c00 : 0; 88912e36acbSWarner Losh 89012e36acbSWarner Losh bwi_tbl_write_2(mac, ofs, 0xfe); 89112e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 1, 0xd); 89212e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 2, 0x13); 89312e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 3, 0x19); 89412e36acbSWarner Losh 89512e36acbSWarner Losh if (phy->phy_rev == 1) { 89612e36acbSWarner Losh bwi_tbl_write_2(mac, 0x1800, 0x2710); 89712e36acbSWarner Losh bwi_tbl_write_2(mac, 0x1801, 0x9b83); 89812e36acbSWarner Losh bwi_tbl_write_2(mac, 0x1802, 0x9b83); 89912e36acbSWarner Losh bwi_tbl_write_2(mac, 0x1803, 0xf8d); 90012e36acbSWarner Losh PHY_WRITE(mac, 0x455, 0x4); 90112e36acbSWarner Losh } 90212e36acbSWarner Losh 90312e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a5, 0xff, 0x5700); 90412e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x41a, 0xff80, 0xf); 90512e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x41a, 0xc07f, 0x2b80); 90612e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x48c, 0xf0ff, 0x300); 90712e36acbSWarner Losh 90812e36acbSWarner Losh RF_SETBITS(mac, 0x7a, 0x8); 90912e36acbSWarner Losh 91012e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a0, 0xfff0, 0x8); 91112e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a1, 0xf0ff, 0x600); 91212e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a2, 0xf0ff, 0x700); 91312e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a0, 0xf0ff, 0x100); 91412e36acbSWarner Losh 91512e36acbSWarner Losh if (phy->phy_rev == 1) 91612e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a2, 0xfff0, 0x7); 91712e36acbSWarner Losh 91812e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x488, 0xff00, 0x1c); 91912e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x488, 0xc0ff, 0x200); 92012e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x496, 0xff00, 0x1c); 92112e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x489, 0xff00, 0x20); 92212e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x489, 0xc0ff, 0x200); 92312e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x482, 0xff00, 0x2e); 92412e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x496, 0xff, 0x1a00); 92512e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x481, 0xff00, 0x28); 92612e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x481, 0xff, 0x2c00); 92712e36acbSWarner Losh 92812e36acbSWarner Losh if (phy->phy_rev == 1) { 92912e36acbSWarner Losh PHY_WRITE(mac, 0x430, 0x92b); 93012e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x41b, 0xffe1, 0x2); 93112e36acbSWarner Losh } else { 93212e36acbSWarner Losh PHY_CLRBITS(mac, 0x41b, 0x1e); 93312e36acbSWarner Losh PHY_WRITE(mac, 0x41f, 0x287a); 93412e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x420, 0xfff0, 0x4); 93512e36acbSWarner Losh 93612e36acbSWarner Losh if (phy->phy_rev >= 6) { 93712e36acbSWarner Losh PHY_WRITE(mac, 0x422, 0x287a); 93812e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x420, 0xfff, 0x3000); 93912e36acbSWarner Losh } 94012e36acbSWarner Losh } 94112e36acbSWarner Losh 94212e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a8, 0x8080, 0x7874); 94312e36acbSWarner Losh PHY_WRITE(mac, 0x48e, 0x1c00); 94412e36acbSWarner Losh 94512e36acbSWarner Losh if (phy->phy_rev == 1) { 94612e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4ab, 0xf0ff, 0x600); 94712e36acbSWarner Losh PHY_WRITE(mac, 0x48b, 0x5e); 94812e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x48c, 0xff00, 0x1e); 94912e36acbSWarner Losh PHY_WRITE(mac, 0x48d, 0x2); 95012e36acbSWarner Losh } 95112e36acbSWarner Losh 95212e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 0x800, 0); 95312e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 0x801, 7); 95412e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 0x802, 16); 95512e36acbSWarner Losh bwi_tbl_write_2(mac, ofs + 0x803, 28); 95612e36acbSWarner Losh 95712e36acbSWarner Losh if (phy->phy_rev >= 6) { 95812e36acbSWarner Losh PHY_CLRBITS(mac, 0x426, 0x3); 95912e36acbSWarner Losh PHY_CLRBITS(mac, 0x426, 0x1000); 96012e36acbSWarner Losh } 96112e36acbSWarner Losh } 96212e36acbSWarner Losh 96312e36acbSWarner Losh void 96412e36acbSWarner Losh bwi_set_gains(struct bwi_mac *mac, const struct bwi_gains *gains) 96512e36acbSWarner Losh { 96612e36acbSWarner Losh struct bwi_phy *phy = &mac->mac_phy; 96712e36acbSWarner Losh uint16_t tbl_gain_ofs1, tbl_gain_ofs2, tbl_gain; 96812e36acbSWarner Losh int i; 96912e36acbSWarner Losh 97012e36acbSWarner Losh if (phy->phy_rev <= 1) { 97112e36acbSWarner Losh tbl_gain_ofs1 = 0x5000; 97212e36acbSWarner Losh tbl_gain_ofs2 = tbl_gain_ofs1 + 16; 97312e36acbSWarner Losh } else { 97412e36acbSWarner Losh tbl_gain_ofs1 = 0x400; 97512e36acbSWarner Losh tbl_gain_ofs2 = tbl_gain_ofs1 + 8; 97612e36acbSWarner Losh } 97712e36acbSWarner Losh 97812e36acbSWarner Losh for (i = 0; i < 4; ++i) { 97912e36acbSWarner Losh if (gains != NULL) { 98012e36acbSWarner Losh tbl_gain = gains->tbl_gain1; 98112e36acbSWarner Losh } else { 98212e36acbSWarner Losh /* Bit swap */ 98312e36acbSWarner Losh tbl_gain = (i & 0x1) << 1; 98412e36acbSWarner Losh tbl_gain |= (i & 0x2) >> 1; 98512e36acbSWarner Losh } 98612e36acbSWarner Losh bwi_tbl_write_2(mac, tbl_gain_ofs1 + i, tbl_gain); 98712e36acbSWarner Losh } 98812e36acbSWarner Losh 98912e36acbSWarner Losh for (i = 0; i < 16; ++i) { 99012e36acbSWarner Losh if (gains != NULL) 99112e36acbSWarner Losh tbl_gain = gains->tbl_gain2; 99212e36acbSWarner Losh else 99312e36acbSWarner Losh tbl_gain = i; 99412e36acbSWarner Losh bwi_tbl_write_2(mac, tbl_gain_ofs2 + i, tbl_gain); 99512e36acbSWarner Losh } 99612e36acbSWarner Losh 99712e36acbSWarner Losh if (gains == NULL || (gains != NULL && gains->phy_gain != -1)) { 99812e36acbSWarner Losh uint16_t phy_gain1, phy_gain2; 99912e36acbSWarner Losh 100012e36acbSWarner Losh if (gains != NULL) { 100112e36acbSWarner Losh phy_gain1 = 100212e36acbSWarner Losh ((uint16_t)gains->phy_gain << 14) | 100312e36acbSWarner Losh ((uint16_t)gains->phy_gain << 6); 100412e36acbSWarner Losh phy_gain2 = phy_gain1; 100512e36acbSWarner Losh } else { 100612e36acbSWarner Losh phy_gain1 = 0x4040; 100712e36acbSWarner Losh phy_gain2 = 0x4000; 100812e36acbSWarner Losh } 100912e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a0, 0xbfbf, phy_gain1); 101012e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a1, 0xbfbf, phy_gain1); 101112e36acbSWarner Losh PHY_FILT_SETBITS(mac, 0x4a2, 0xbfbf, phy_gain2); 101212e36acbSWarner Losh } 101312e36acbSWarner Losh bwi_mac_dummy_xmit(mac); 101412e36acbSWarner Losh } 101512e36acbSWarner Losh 101612e36acbSWarner Losh void 101712e36acbSWarner Losh bwi_phy_clear_state(struct bwi_phy *phy) 101812e36acbSWarner Losh { 101912e36acbSWarner Losh phy->phy_flags &= ~BWI_CLEAR_PHY_FLAGS; 102012e36acbSWarner Losh } 1021