android_kernel_samsung_msm8976/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
Larry Finger 740ac4fb08 [PATCH] bcm43xx: Fix for 4311 and 02/07/07 specification changes
The specifications for the bcm43xx driver have been modified. This
patch incorporates these changes in the code, which results in the
BCM4311 and BCM4312 working. The name of one of the PHY parameters,
previously known as "version", has been changed to "analog", short for
"analog core version" .

Signed-off-by: Larry Finger<Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2007-02-14 15:45:05 -05:00

2328 lines
69 KiB
C

/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/types.h>
#include "bcm43xx.h"
#include "bcm43xx_phy.h"
#include "bcm43xx_main.h"
#include "bcm43xx_radio.h"
#include "bcm43xx_ilt.h"
#include "bcm43xx_power.h"
static const s8 bcm43xx_tssi2dbm_b_table[] = {
0x4D, 0x4C, 0x4B, 0x4A,
0x4A, 0x49, 0x48, 0x47,
0x47, 0x46, 0x45, 0x45,
0x44, 0x43, 0x42, 0x42,
0x41, 0x40, 0x3F, 0x3E,
0x3D, 0x3C, 0x3B, 0x3A,
0x39, 0x38, 0x37, 0x36,
0x35, 0x34, 0x32, 0x31,
0x30, 0x2F, 0x2D, 0x2C,
0x2B, 0x29, 0x28, 0x26,
0x25, 0x23, 0x21, 0x1F,
0x1D, 0x1A, 0x17, 0x14,
0x10, 0x0C, 0x06, 0x00,
-7, -7, -7, -7,
-7, -7, -7, -7,
-7, -7, -7, -7,
};
static const s8 bcm43xx_tssi2dbm_g_table[] = {
77, 77, 77, 76,
76, 76, 75, 75,
74, 74, 73, 73,
73, 72, 72, 71,
71, 70, 70, 69,
68, 68, 67, 67,
66, 65, 65, 64,
63, 63, 62, 61,
60, 59, 58, 57,
56, 55, 54, 53,
52, 50, 49, 47,
45, 43, 40, 37,
33, 28, 22, 14,
5, -7, -20, -20,
-20, -20, -20, -20,
-20, -20, -20, -20,
};
static void bcm43xx_phy_initg(struct bcm43xx_private *bcm);
static inline
void bcm43xx_voluntary_preempt(void)
{
assert(!in_atomic() && !in_irq() &&
!in_interrupt() && !irqs_disabled());
#ifndef CONFIG_PREEMPT
cond_resched();
#endif /* CONFIG_PREEMPT */
}
void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
assert(irqs_disabled());
if (bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) == 0x00000000) {
phy->is_locked = 0;
return;
}
if (bcm->current_core->rev < 3) {
bcm43xx_mac_suspend(bcm);
spin_lock(&phy->lock);
} else {
if (bcm->ieee->iw_mode != IW_MODE_MASTER)
bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
}
phy->is_locked = 1;
}
void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
assert(irqs_disabled());
if (bcm->current_core->rev < 3) {
if (phy->is_locked) {
spin_unlock(&phy->lock);
bcm43xx_mac_enable(bcm);
}
} else {
if (bcm->ieee->iw_mode != IW_MODE_MASTER)
bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
}
phy->is_locked = 0;
}
u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset)
{
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset);
return bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_DATA);
}
void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val)
{
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset);
mmiowb();
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_DATA, val);
}
void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */
if (phy->calibrated)
return;
if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) {
bcm43xx_wireless_core_reset(bcm, 0);
bcm43xx_phy_initg(bcm);
bcm43xx_wireless_core_reset(bcm, 1);
}
phy->calibrated = 1;
}
/* Connect the PHY
* http://bcm-specs.sipsolutions.net/SetPHY
*/
int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u32 flags;
if (bcm->current_core->rev < 5)
goto out;
flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
if (connect) {
if (!(flags & 0x00010000))
return -ENODEV;
flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
flags |= (0x800 << 18);
bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
} else {
if (!(flags & 0x00020000))
return -ENODEV;
flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
flags &= ~(0x800 << 18);
bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
}
out:
phy->connected = connect;
if (connect)
dprintk(KERN_INFO PFX "PHY connected\n");
else
dprintk(KERN_INFO PFX "PHY disconnected\n");
return 0;
}
/* intialize B PHY power control
* as described in http://bcm-specs.sipsolutions.net/InitPowerControl
*/
static void bcm43xx_phy_init_pctl(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 saved_batt = 0, saved_ratt = 0, saved_txctl1 = 0;
int must_reset_txpower = 0;
assert(phy->type != BCM43xx_PHYTYPE_A);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type == 0x0416))
return;
bcm43xx_phy_write(bcm, 0x0028, 0x8018);
bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF);
if (phy->type == BCM43xx_PHYTYPE_G) {
if (!phy->connected)
return;
bcm43xx_phy_write(bcm, 0x047A, 0xC111);
}
if (phy->savedpctlreg != 0xFFFF)
return;
if (phy->type == BCM43xx_PHYTYPE_B &&
phy->rev >= 2 &&
radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0076,
bcm43xx_radio_read16(bcm, 0x0076) | 0x0084);
} else {
saved_batt = radio->baseband_atten;
saved_ratt = radio->radio_atten;
saved_txctl1 = radio->txctl1;
if ((radio->revision >= 6) && (radio->revision <= 8)
&& /*FIXME: incomplete specs for 5 < revision < 9 */ 0)
bcm43xx_radio_set_txpower_bg(bcm, 0xB, 0x1F, 0);
else
bcm43xx_radio_set_txpower_bg(bcm, 0xB, 9, 0);
must_reset_txpower = 1;
}
bcm43xx_dummy_transmission(bcm);
phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_PCTL);
if (must_reset_txpower)
bcm43xx_radio_set_txpower_bg(bcm, saved_batt, saved_ratt, saved_txctl1);
else
bcm43xx_radio_write16(bcm, 0x0076, bcm43xx_radio_read16(bcm, 0x0076) & 0xFF7B);
bcm43xx_radio_clear_tssi(bcm);
}
static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 offset = 0x0000;
if (phy->rev == 1)
offset = 0x4C00;
bcm43xx_ilt_write(bcm, offset, 0x00FE);
bcm43xx_ilt_write(bcm, offset + 1, 0x000D);
bcm43xx_ilt_write(bcm, offset + 2, 0x0013);
bcm43xx_ilt_write(bcm, offset + 3, 0x0019);
if (phy->rev == 1) {
bcm43xx_ilt_write(bcm, 0x1800, 0x2710);
bcm43xx_ilt_write(bcm, 0x1801, 0x9B83);
bcm43xx_ilt_write(bcm, 0x1802, 0x9B83);
bcm43xx_ilt_write(bcm, 0x1803, 0x0F8D);
bcm43xx_phy_write(bcm, 0x0455, 0x0004);
}
bcm43xx_phy_write(bcm, 0x04A5, (bcm43xx_phy_read(bcm, 0x04A5) & 0x00FF) | 0x5700);
bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xFF80) | 0x000F);
bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xC07F) | 0x2B80);
bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xF0FF) | 0x0300);
bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0008);
bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xFFF0) | 0x0008);
bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xF0FF) | 0x0600);
bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xF0FF) | 0x0700);
bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xF0FF) | 0x0100);
if (phy->rev == 1)
bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xFFF0) | 0x0007);
bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xFF00) | 0x001C);
bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xC0FF) | 0x0200);
bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0xFF00) | 0x001C);
bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xFF00) | 0x0020);
bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xC0FF) | 0x0200);
bcm43xx_phy_write(bcm, 0x0482, (bcm43xx_phy_read(bcm, 0x0482) & 0xFF00) | 0x002E);
bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0x00FF) | 0x1A00);
bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0xFF00) | 0x0028);
bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0x00FF) | 0x2C00);
if (phy->rev == 1) {
bcm43xx_phy_write(bcm, 0x0430, 0x092B);
bcm43xx_phy_write(bcm, 0x041B, (bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1) | 0x0002);
} else {
bcm43xx_phy_write(bcm, 0x041B, bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1);
bcm43xx_phy_write(bcm, 0x041F, 0x287A);
bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0xFFF0) | 0x0004);
}
if (phy->rev > 2) {
bcm43xx_phy_write(bcm, 0x0422, 0x287A);
bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000);
}
bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874);
bcm43xx_phy_write(bcm, 0x048E, 0x1C00);
if (phy->rev == 1) {
bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600);
bcm43xx_phy_write(bcm, 0x048B, 0x005E);
bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E);
bcm43xx_phy_write(bcm, 0x048D, 0x0002);
}
bcm43xx_ilt_write(bcm, offset + 0x0800, 0);
bcm43xx_ilt_write(bcm, offset + 0x0801, 7);
bcm43xx_ilt_write(bcm, offset + 0x0802, 16);
bcm43xx_ilt_write(bcm, offset + 0x0803, 28);
if (phy->rev >= 6) {
bcm43xx_phy_write(bcm, 0x0426, (bcm43xx_phy_read(bcm, 0x0426)
& 0xFFFC));
bcm43xx_phy_write(bcm, 0x0426, (bcm43xx_phy_read(bcm, 0x0426)
& 0xEFFF));
}
}
static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 i;
assert(phy->type == BCM43xx_PHYTYPE_G);
if (phy->rev == 1) {
bcm43xx_phy_write(bcm, 0x0406, 0x4F19);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
(bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340);
bcm43xx_phy_write(bcm, 0x042C, 0x005A);
bcm43xx_phy_write(bcm, 0x0427, 0x001A);
for (i = 0; i < BCM43xx_ILT_FINEFREQG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqg[i]);
for (i = 0; i < BCM43xx_ILT_NOISEG1_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg1[i]);
for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]);
} else {
/* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */
bcm43xx_nrssi_hw_write(bcm, 0xBA98, (s16)0x7654);
if (phy->rev == 2) {
bcm43xx_phy_write(bcm, 0x04C0, 0x1861);
bcm43xx_phy_write(bcm, 0x04C1, 0x0271);
} else if (phy->rev > 2) {
bcm43xx_phy_write(bcm, 0x04C0, 0x0098);
bcm43xx_phy_write(bcm, 0x04C1, 0x0070);
bcm43xx_phy_write(bcm, 0x04C9, 0x0080);
}
bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x800);
for (i = 0; i < 64; i++)
bcm43xx_ilt_write(bcm, 0x4000 + i, i);
for (i = 0; i < BCM43xx_ILT_NOISEG2_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg2[i]);
}
if (phy->rev <= 2)
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]);
else if ((phy->rev >= 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]);
else
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg2[i]);
if (phy->rev == 2)
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
else if ((phy->rev > 2) && (phy->rev <= 8))
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]);
if (phy->rev == 1) {
for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]);
for (i = 0; i < 4; i++) {
bcm43xx_ilt_write(bcm, 0x5404 + i, 0x0020);
bcm43xx_ilt_write(bcm, 0x5408 + i, 0x0020);
bcm43xx_ilt_write(bcm, 0x540C + i, 0x0020);
bcm43xx_ilt_write(bcm, 0x5410 + i, 0x0020);
}
bcm43xx_phy_agcsetup(bcm);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type == 0x0416) &&
(bcm->board_revision == 0x0017))
return;
bcm43xx_ilt_write(bcm, 0x5001, 0x0002);
bcm43xx_ilt_write(bcm, 0x5002, 0x0001);
} else {
for (i = 0; i <= 0x2F; i++)
bcm43xx_ilt_write(bcm, 0x1000 + i, 0x0820);
bcm43xx_phy_agcsetup(bcm);
bcm43xx_phy_read(bcm, 0x0400); /* dummy read */
bcm43xx_phy_write(bcm, 0x0403, 0x1000);
bcm43xx_ilt_write(bcm, 0x3C02, 0x000F);
bcm43xx_ilt_write(bcm, 0x3C03, 0x0014);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type == 0x0416) &&
(bcm->board_revision == 0x0017))
return;
bcm43xx_ilt_write(bcm, 0x0401, 0x0002);
bcm43xx_ilt_write(bcm, 0x0402, 0x0001);
}
}
/* Initialize the noisescaletable for APHY */
static void bcm43xx_phy_init_noisescaletbl(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int i;
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, 0x1400);
for (i = 0; i < 12; i++) {
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323);
}
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6700);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2300);
for (i = 0; i < 11; i++) {
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323);
}
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0067);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0023);
}
static void bcm43xx_phy_setupa(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 i;
assert(phy->type == BCM43xx_PHYTYPE_A);
switch (phy->rev) {
case 2:
bcm43xx_phy_write(bcm, 0x008E, 0x3800);
bcm43xx_phy_write(bcm, 0x0035, 0x03FF);
bcm43xx_phy_write(bcm, 0x0036, 0x0400);
bcm43xx_ilt_write(bcm, 0x3807, 0x0051);
bcm43xx_phy_write(bcm, 0x001C, 0x0FF9);
bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F);
bcm43xx_ilt_write(bcm, 0x3C0C, 0x07BF);
bcm43xx_radio_write16(bcm, 0x0002, 0x07BF);
bcm43xx_phy_write(bcm, 0x0024, 0x4680);
bcm43xx_phy_write(bcm, 0x0020, 0x0003);
bcm43xx_phy_write(bcm, 0x001D, 0x0F40);
bcm43xx_phy_write(bcm, 0x001F, 0x1C00);
bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400);
bcm43xx_phy_write(bcm, 0x002B, bcm43xx_phy_read(bcm, 0x002B) & 0xFBFF);
bcm43xx_phy_write(bcm, 0x008E, 0x58C1);
bcm43xx_ilt_write(bcm, 0x0803, 0x000F);
bcm43xx_ilt_write(bcm, 0x0804, 0x001F);
bcm43xx_ilt_write(bcm, 0x0805, 0x002A);
bcm43xx_ilt_write(bcm, 0x0805, 0x0030);
bcm43xx_ilt_write(bcm, 0x0807, 0x003A);
bcm43xx_ilt_write(bcm, 0x0000, 0x0013);
bcm43xx_ilt_write(bcm, 0x0001, 0x0013);
bcm43xx_ilt_write(bcm, 0x0002, 0x0013);
bcm43xx_ilt_write(bcm, 0x0003, 0x0013);
bcm43xx_ilt_write(bcm, 0x0004, 0x0015);
bcm43xx_ilt_write(bcm, 0x0005, 0x0015);
bcm43xx_ilt_write(bcm, 0x0006, 0x0019);
bcm43xx_ilt_write(bcm, 0x0404, 0x0003);
bcm43xx_ilt_write(bcm, 0x0405, 0x0003);
bcm43xx_ilt_write(bcm, 0x0406, 0x0007);
for (i = 0; i < 16; i++)
bcm43xx_ilt_write(bcm, 0x4000 + i, (0x8 + i) & 0x000F);
bcm43xx_ilt_write(bcm, 0x3003, 0x1044);
bcm43xx_ilt_write(bcm, 0x3004, 0x7201);
bcm43xx_ilt_write(bcm, 0x3006, 0x0040);
bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008);
for (i = 0; i < BCM43xx_ILT_FINEFREQA_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqa[i]);
for (i = 0; i < BCM43xx_ILT_NOISEA2_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea2[i]);
for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]);
bcm43xx_phy_init_noisescaletbl(bcm);
for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]);
break;
case 3:
for (i = 0; i < 64; i++)
bcm43xx_ilt_write(bcm, 0x4000 + i, i);
bcm43xx_ilt_write(bcm, 0x3807, 0x0051);
bcm43xx_phy_write(bcm, 0x001C, 0x0FF9);
bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F);
bcm43xx_radio_write16(bcm, 0x0002, 0x07BF);
bcm43xx_phy_write(bcm, 0x0024, 0x4680);
bcm43xx_phy_write(bcm, 0x0020, 0x0003);
bcm43xx_phy_write(bcm, 0x001D, 0x0F40);
bcm43xx_phy_write(bcm, 0x001F, 0x1C00);
bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400);
bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008);
for (i = 0; i < BCM43xx_ILT_NOISEA3_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea3[i]);
bcm43xx_phy_init_noisescaletbl(bcm);
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
bcm43xx_phy_write(bcm, 0x0003, 0x1808);
bcm43xx_ilt_write(bcm, 0x0803, 0x000F);
bcm43xx_ilt_write(bcm, 0x0804, 0x001F);
bcm43xx_ilt_write(bcm, 0x0805, 0x002A);
bcm43xx_ilt_write(bcm, 0x0805, 0x0030);
bcm43xx_ilt_write(bcm, 0x0807, 0x003A);
bcm43xx_ilt_write(bcm, 0x0000, 0x0013);
bcm43xx_ilt_write(bcm, 0x0001, 0x0013);
bcm43xx_ilt_write(bcm, 0x0002, 0x0013);
bcm43xx_ilt_write(bcm, 0x0003, 0x0013);
bcm43xx_ilt_write(bcm, 0x0004, 0x0015);
bcm43xx_ilt_write(bcm, 0x0005, 0x0015);
bcm43xx_ilt_write(bcm, 0x0006, 0x0019);
bcm43xx_ilt_write(bcm, 0x0404, 0x0003);
bcm43xx_ilt_write(bcm, 0x0405, 0x0003);
bcm43xx_ilt_write(bcm, 0x0406, 0x0007);
bcm43xx_ilt_write(bcm, 0x3C02, 0x000F);
bcm43xx_ilt_write(bcm, 0x3C03, 0x0014);
break;
default:
assert(0);
}
}
/* Initialize APHY. This is also called for the GPHY in some cases. */
static void bcm43xx_phy_inita(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 tval;
if (phy->type == BCM43xx_PHYTYPE_A) {
bcm43xx_phy_setupa(bcm);
} else {
bcm43xx_phy_setupg(bcm);
if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
bcm43xx_phy_write(bcm, 0x046E, 0x03CF);
return;
}
bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS,
(bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) & 0xF83C) | 0x0340);
bcm43xx_phy_write(bcm, 0x0034, 0x0001);
TODO();//TODO: RSSI AGC
bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) | (1 << 14));
bcm43xx_radio_init2060(bcm);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM)
&& ((bcm->board_type == 0x0416) || (bcm->board_type == 0x040A))) {
if (radio->lofcal == 0xFFFF) {
TODO();//TODO: LOF Cal
bcm43xx_radio_set_tx_iq(bcm);
} else
bcm43xx_radio_write16(bcm, 0x001E, radio->lofcal);
}
bcm43xx_phy_write(bcm, 0x007A, 0xF111);
if (phy->savedpctlreg == 0xFFFF) {
bcm43xx_radio_write16(bcm, 0x0019, 0x0000);
bcm43xx_radio_write16(bcm, 0x0017, 0x0020);
tval = bcm43xx_ilt_read(bcm, 0x3001);
if (phy->rev == 1) {
bcm43xx_ilt_write(bcm, 0x3001,
(bcm43xx_ilt_read(bcm, 0x3001) & 0xFF87)
| 0x0058);
} else {
bcm43xx_ilt_write(bcm, 0x3001,
(bcm43xx_ilt_read(bcm, 0x3001) & 0xFFC3)
| 0x002C);
}
bcm43xx_dummy_transmission(bcm);
phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_A_PCTL);
bcm43xx_ilt_write(bcm, 0x3001, tval);
bcm43xx_radio_set_txpower_a(bcm, 0x0018);
}
bcm43xx_radio_clear_tssi(bcm);
}
static void bcm43xx_phy_initb2(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset, val;
bcm43xx_write16(bcm, 0x03EC, 0x3F22);
bcm43xx_phy_write(bcm, 0x0020, 0x301C);
bcm43xx_phy_write(bcm, 0x0026, 0x0000);
bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
bcm43xx_phy_write(bcm, 0x0088, 0x3E00);
val = 0x3C3D;
for (offset = 0x0089; offset < 0x00A7; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
bcm43xx_phy_write(bcm, 0x03E4, 0x3000);
if (radio->channel == 0xFF)
bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
else
bcm43xx_radio_selectchannel(bcm, radio->channel, 0);
if (radio->version != 0x2050) {
bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
}
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
bcm43xx_radio_write16(bcm, 0x007A, 0x000F);
bcm43xx_phy_write(bcm, 0x0038, 0x0677);
bcm43xx_radio_init2050(bcm);
}
bcm43xx_phy_write(bcm, 0x0014, 0x0080);
bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
bcm43xx_phy_write(bcm, 0x0032, 0x00CC);
bcm43xx_phy_write(bcm, 0x0035, 0x07C2);
bcm43xx_phy_lo_b_measure(bcm);
bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
if (radio->version != 0x2050)
bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1000);
bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
if (radio->version != 0x2050)
bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
bcm43xx_phy_init_pctl(bcm);
}
static void bcm43xx_phy_initb4(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset, val;
bcm43xx_write16(bcm, 0x03EC, 0x3F22);
bcm43xx_phy_write(bcm, 0x0020, 0x301C);
bcm43xx_phy_write(bcm, 0x0026, 0x0000);
bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
bcm43xx_phy_write(bcm, 0x0088, 0x3E00);
val = 0x3C3D;
for (offset = 0x0089; offset < 0x00A7; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
bcm43xx_phy_write(bcm, 0x03E4, 0x3000);
if (radio->channel == 0xFF)
bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
else
bcm43xx_radio_selectchannel(bcm, radio->channel, 0);
if (radio->version != 0x2050) {
bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
}
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
bcm43xx_radio_write16(bcm, 0x007A, 0x000F);
bcm43xx_phy_write(bcm, 0x0038, 0x0677);
bcm43xx_radio_init2050(bcm);
}
bcm43xx_phy_write(bcm, 0x0014, 0x0080);
bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x0032, 0x00E0);
bcm43xx_phy_write(bcm, 0x0035, 0x07C2);
bcm43xx_phy_lo_b_measure(bcm);
bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1100);
bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
bcm43xx_calc_nrssi_slope(bcm);
bcm43xx_calc_nrssi_threshold(bcm);
}
bcm43xx_phy_init_pctl(bcm);
}
static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset;
u16 value;
u8 old_channel;
if (phy->analog == 1)
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A)
| 0x0050);
if ((bcm->board_vendor != PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type != 0x0416)) {
value = 0x2120;
for (offset = 0x00A8 ; offset < 0x00C7; offset++) {
bcm43xx_phy_write(bcm, offset, value);
value += 0x0202;
}
}
bcm43xx_phy_write(bcm, 0x0035,
(bcm43xx_phy_read(bcm, 0x0035) & 0xF0FF)
| 0x0700);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x0038, 0x0667);
if (phy->type == BCM43xx_PHYTYPE_G) {
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A)
| 0x0020);
bcm43xx_radio_write16(bcm, 0x0051,
bcm43xx_radio_read16(bcm, 0x0051)
| 0x0004);
}
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, 0x0000);
bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) | 0x0100);
bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x2000);
bcm43xx_phy_write(bcm, 0x001C, 0x186A);
bcm43xx_phy_write(bcm, 0x0013, (bcm43xx_phy_read(bcm, 0x0013) & 0x00FF) | 0x1900);
bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFFC0) | 0x0064);
bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D) & 0xFF80) | 0x000A);
}
if (bcm->bad_frames_preempt) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11));
}
if (phy->analog == 1) {
bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
bcm43xx_phy_write(bcm, 0x0021, 0x3763);
bcm43xx_phy_write(bcm, 0x0022, 0x1BC3);
bcm43xx_phy_write(bcm, 0x0023, 0x06F9);
bcm43xx_phy_write(bcm, 0x0024, 0x037E);
} else
bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
bcm43xx_write16(bcm, 0x03EC, 0x3F22);
if (phy->analog == 1)
bcm43xx_phy_write(bcm, 0x0020, 0x3E1C);
else
bcm43xx_phy_write(bcm, 0x0020, 0x301C);
if (phy->analog == 0)
bcm43xx_write16(bcm, 0x03E4, 0x3000);
old_channel = radio->channel;
/* Force to channel 7, even if not supported. */
bcm43xx_radio_selectchannel(bcm, 7, 0);
if (radio->version != 0x2050) {
bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
}
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
}
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0007);
bcm43xx_radio_selectchannel(bcm, old_channel, 0);
bcm43xx_phy_write(bcm, 0x0014, 0x0080);
bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
if (radio->version == 0x2050)
bcm43xx_radio_write16(bcm, 0x005D, 0x000D);
bcm43xx_write16(bcm, 0x03E4, (bcm43xx_read16(bcm, 0x03E4) & 0xFFC0) | 0x0004);
}
static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset, val;
u8 old_channel;
bcm43xx_phy_write(bcm, 0x003E, 0x817A);
bcm43xx_radio_write16(bcm, 0x007A,
(bcm43xx_radio_read16(bcm, 0x007A) | 0x0058));
if (radio->revision == 4 ||
radio->revision == 5) {
bcm43xx_radio_write16(bcm, 0x0051, 0x0037);
bcm43xx_radio_write16(bcm, 0x0052, 0x0070);
bcm43xx_radio_write16(bcm, 0x0053, 0x00B3);
bcm43xx_radio_write16(bcm, 0x0054, 0x009B);
bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
bcm43xx_radio_write16(bcm, 0x005B, 0x0088);
bcm43xx_radio_write16(bcm, 0x005D, 0x0088);
bcm43xx_radio_write16(bcm, 0x005E, 0x0088);
bcm43xx_radio_write16(bcm, 0x007D, 0x0088);
}
if (radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0051, 0x0000);
bcm43xx_radio_write16(bcm, 0x0052, 0x0040);
bcm43xx_radio_write16(bcm, 0x0053, 0x00B7);
bcm43xx_radio_write16(bcm, 0x0054, 0x0098);
bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
bcm43xx_radio_write16(bcm, 0x005B, 0x006B);
bcm43xx_radio_write16(bcm, 0x005C, 0x000F);
if (bcm->sprom.boardflags & 0x8000) {
bcm43xx_radio_write16(bcm, 0x005D, 0x00FA);
bcm43xx_radio_write16(bcm, 0x005E, 0x00D8);
} else {
bcm43xx_radio_write16(bcm, 0x005D, 0x00F5);
bcm43xx_radio_write16(bcm, 0x005E, 0x00B8);
}
bcm43xx_radio_write16(bcm, 0x0073, 0x0003);
bcm43xx_radio_write16(bcm, 0x007D, 0x00A8);
bcm43xx_radio_write16(bcm, 0x007C, 0x0001);
bcm43xx_radio_write16(bcm, 0x007E, 0x0008);
}
val = 0x1E1F;
for (offset = 0x0088; offset < 0x0098; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
val = 0x3E3F;
for (offset = 0x0098; offset < 0x00A8; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
val = 0x2120;
for (offset = 0x00A8; offset < 0x00C8; offset++) {
bcm43xx_phy_write(bcm, offset, (val & 0x3F3F));
val += 0x0202;
}
if (phy->type == BCM43xx_PHYTYPE_G) {
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0020);
bcm43xx_radio_write16(bcm, 0x0051,
bcm43xx_radio_read16(bcm, 0x0051) | 0x0004);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) | 0x0100);
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B) | 0x2000);
bcm43xx_phy_write(bcm, 0x5B, 0x0000);
bcm43xx_phy_write(bcm, 0x5C, 0x0000);
}
old_channel = radio->channel;
if (old_channel >= 8)
bcm43xx_radio_selectchannel(bcm, 1, 0);
else
bcm43xx_radio_selectchannel(bcm, 13, 0);
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
udelay(40);
if (radio->revision < 6 || radio-> revision == 8) {
bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C)
| 0x0002));
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
}
if (radio->revision <= 2) {
bcm43xx_radio_write16(bcm, 0x007C, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
}
bcm43xx_radio_write16(bcm, 0x007A,
(bcm43xx_radio_read16(bcm, 0x007A) & 0x00F8) | 0x0007);
bcm43xx_radio_selectchannel(bcm, old_channel, 0);
bcm43xx_phy_write(bcm, 0x0014, 0x0200);
if (radio->revision >= 6)
bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
else
bcm43xx_phy_write(bcm, 0x002A, 0x8AC0);
bcm43xx_phy_write(bcm, 0x0038, 0x0668);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
if (radio->revision <= 5)
bcm43xx_phy_write(bcm, 0x005D, bcm43xx_phy_read(bcm, 0x005D) | 0x0003);
if (radio->revision <= 2)
bcm43xx_radio_write16(bcm, 0x005D, 0x000D);
if (phy->analog == 4){
bcm43xx_write16(bcm, 0x03E4, 0x0009);
bcm43xx_phy_write(bcm, 0x61, bcm43xx_phy_read(bcm, 0x61) & 0xFFF);
} else {
bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004);
}
if (phy->type == BCM43xx_PHYTYPE_G)
bcm43xx_write16(bcm, 0x03E6, 0x0);
if (phy->type == BCM43xx_PHYTYPE_B) {
bcm43xx_write16(bcm, 0x03E6, 0x8140);
bcm43xx_phy_write(bcm, 0x0016, 0x0410);
bcm43xx_phy_write(bcm, 0x0017, 0x0820);
bcm43xx_phy_write(bcm, 0x0062, 0x0007);
(void) bcm43xx_radio_calibrationvalue(bcm);
bcm43xx_phy_lo_g_measure(bcm);
if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
bcm43xx_calc_nrssi_slope(bcm);
bcm43xx_calc_nrssi_threshold(bcm);
}
bcm43xx_phy_init_pctl(bcm);
}
}
static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 backup_phy[15];
u16 backup_radio[3];
u16 backup_bband;
u16 i;
u16 loop1_cnt, loop1_done, loop1_omitted;
u16 loop2_done;
backup_phy[0] = bcm43xx_phy_read(bcm, 0x0429);
backup_phy[1] = bcm43xx_phy_read(bcm, 0x0001);
backup_phy[2] = bcm43xx_phy_read(bcm, 0x0811);
backup_phy[3] = bcm43xx_phy_read(bcm, 0x0812);
backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814);
backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815);
backup_phy[6] = bcm43xx_phy_read(bcm, 0x005A);
backup_phy[7] = bcm43xx_phy_read(bcm, 0x0059);
backup_phy[8] = bcm43xx_phy_read(bcm, 0x0058);
backup_phy[9] = bcm43xx_phy_read(bcm, 0x000A);
backup_phy[10] = bcm43xx_phy_read(bcm, 0x0003);
backup_phy[11] = bcm43xx_phy_read(bcm, 0x080F);
backup_phy[12] = bcm43xx_phy_read(bcm, 0x0810);
backup_phy[13] = bcm43xx_phy_read(bcm, 0x002B);
backup_phy[14] = bcm43xx_phy_read(bcm, 0x0015);
bcm43xx_phy_read(bcm, 0x002D); /* dummy read */
backup_bband = radio->baseband_atten;
backup_radio[0] = bcm43xx_radio_read16(bcm, 0x0052);
backup_radio[1] = bcm43xx_radio_read16(bcm, 0x0043);
backup_radio[2] = bcm43xx_radio_read16(bcm, 0x007A);
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) & 0x3FFF);
bcm43xx_phy_write(bcm, 0x0001,
bcm43xx_phy_read(bcm, 0x0001) & 0x8000);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0002);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xFFFD);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0001);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xFFFE);
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0001);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE);
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0002);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) | 0x000C);
bcm43xx_phy_write(bcm, 0x0811,
(bcm43xx_phy_read(bcm, 0x0811)
& 0xFFCF) | 0x0030);
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812)
& 0xFFCF) | 0x0010);
bcm43xx_phy_write(bcm, 0x005A, 0x0780);
bcm43xx_phy_write(bcm, 0x0059, 0xC810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
if (phy->analog == 0) {
bcm43xx_phy_write(bcm, 0x0003, 0x0122);
} else {
bcm43xx_phy_write(bcm, 0x000A,
bcm43xx_phy_read(bcm, 0x000A)
| 0x2000);
}
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0004);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB);
bcm43xx_phy_write(bcm, 0x0003,
(bcm43xx_phy_read(bcm, 0x0003)
& 0xFF9F) | 0x0040);
if (radio->version == 0x2050 && radio->revision == 2) {
bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
bcm43xx_radio_write16(bcm, 0x0043,
(bcm43xx_radio_read16(bcm, 0x0043)
& 0xFFF0) | 0x0009);
loop1_cnt = 9;
} else if (radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0043, 0x000F);
loop1_cnt = 15;
} else
loop1_cnt = 0;
bcm43xx_phy_set_baseband_attenuation(bcm, 11);
if (phy->rev >= 3)
bcm43xx_phy_write(bcm, 0x080F, 0xC020);
else
bcm43xx_phy_write(bcm, 0x080F, 0x8020);
bcm43xx_phy_write(bcm, 0x0810, 0x0000);
bcm43xx_phy_write(bcm, 0x002B,
(bcm43xx_phy_read(bcm, 0x002B)
& 0xFFC0) | 0x0001);
bcm43xx_phy_write(bcm, 0x002B,
(bcm43xx_phy_read(bcm, 0x002B)
& 0xC0FF) | 0x0800);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0100);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xCFFF);
if (bcm->sprom.boardflags & BCM43xx_BFL_EXTLNA) {
if (phy->rev >= 7) {
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811)
| 0x0800);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812)
| 0x8000);
}
}
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A)
& 0x00F7);
for (i = 0; i < loop1_cnt; i++) {
bcm43xx_radio_write16(bcm, 0x0043, loop1_cnt);
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812)
& 0xF0FF) | (i << 8));
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xA000);
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xF000);
udelay(20);
if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC)
break;
}
loop1_done = i;
loop1_omitted = loop1_cnt - loop1_done;
loop2_done = 0;
if (loop1_done >= 8) {
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812)
| 0x0030);
for (i = loop1_done - 8; i < 16; i++) {
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812)
& 0xF0FF) | (i << 8));
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xA000);
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xF000);
udelay(20);
if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC)
break;
}
}
bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]);
bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]);
bcm43xx_phy_write(bcm, 0x005A, backup_phy[6]);
bcm43xx_phy_write(bcm, 0x0059, backup_phy[7]);
bcm43xx_phy_write(bcm, 0x0058, backup_phy[8]);
bcm43xx_phy_write(bcm, 0x000A, backup_phy[9]);
bcm43xx_phy_write(bcm, 0x0003, backup_phy[10]);
bcm43xx_phy_write(bcm, 0x080F, backup_phy[11]);
bcm43xx_phy_write(bcm, 0x0810, backup_phy[12]);
bcm43xx_phy_write(bcm, 0x002B, backup_phy[13]);
bcm43xx_phy_write(bcm, 0x0015, backup_phy[14]);
bcm43xx_phy_set_baseband_attenuation(bcm, backup_bband);
bcm43xx_radio_write16(bcm, 0x0052, backup_radio[0]);
bcm43xx_radio_write16(bcm, 0x0043, backup_radio[1]);
bcm43xx_radio_write16(bcm, 0x007A, backup_radio[2]);
bcm43xx_phy_write(bcm, 0x0811, backup_phy[2] | 0x0003);
udelay(10);
bcm43xx_phy_write(bcm, 0x0811, backup_phy[2]);
bcm43xx_phy_write(bcm, 0x0812, backup_phy[3]);
bcm43xx_phy_write(bcm, 0x0429, backup_phy[0]);
bcm43xx_phy_write(bcm, 0x0001, backup_phy[1]);
phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11;
phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2;
}
static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 tmp;
if (phy->rev == 1)
bcm43xx_phy_initb5(bcm);
else
bcm43xx_phy_initb6(bcm);
if (phy->rev >= 2 || phy->connected)
bcm43xx_phy_inita(bcm);
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0814, 0x0000);
bcm43xx_phy_write(bcm, 0x0815, 0x0000);
}
if (phy->rev == 2) {
bcm43xx_phy_write(bcm, 0x0811, 0x0000);
bcm43xx_phy_write(bcm, 0x0015, 0x00C0);
}
if (phy->rev >= 3) {
bcm43xx_phy_write(bcm, 0x0811, 0x0400);
bcm43xx_phy_write(bcm, 0x0015, 0x00C0);
}
if (phy->connected) {
tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF;
if (tmp < 6) {
bcm43xx_phy_write(bcm, 0x04C2, 0x1816);
bcm43xx_phy_write(bcm, 0x04C3, 0x8006);
if (tmp != 3) {
bcm43xx_phy_write(bcm, 0x04CC,
(bcm43xx_phy_read(bcm, 0x04CC)
& 0x00FF) | 0x1F00);
}
}
}
if (phy->rev < 3 && phy->connected)
bcm43xx_phy_write(bcm, 0x047E, 0x0078);
if (radio->revision == 8) {
bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0080);
bcm43xx_phy_write(bcm, 0x043E, bcm43xx_phy_read(bcm, 0x043E) | 0x0004);
}
if (phy->rev >= 2 && phy->connected)
bcm43xx_calc_loopback_gain(bcm);
if (radio->revision != 8) {
if (radio->initval == 0xFFFF)
radio->initval = bcm43xx_radio_init2050(bcm);
else
bcm43xx_radio_write16(bcm, 0x0078, radio->initval);
}
if (radio->txctl2 == 0xFFFF) {
bcm43xx_phy_lo_g_measure(bcm);
} else {
if (radio->version == 0x2050 && radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0052,
(radio->txctl1 << 4) | radio->txctl2);
} else {
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052)
& 0xFFF0) | radio->txctl1);
}
if (phy->rev >= 6) {
bcm43xx_phy_write(bcm, 0x0036,
(bcm43xx_phy_read(bcm, 0x0036)
& 0xF000) | (radio->txctl2 << 12));
}
if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
bcm43xx_phy_write(bcm, 0x002E, 0x8075);
else
bcm43xx_phy_write(bcm, 0x002E, 0x807F);
if (phy->rev < 2)
bcm43xx_phy_write(bcm, 0x002F, 0x0101);
else
bcm43xx_phy_write(bcm, 0x002F, 0x0202);
}
if (phy->connected) {
bcm43xx_phy_lo_adjust(bcm, 0);
bcm43xx_phy_write(bcm, 0x080F, 0x8078);
}
if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) {
/* The specs state to update the NRSSI LT with
* the value 0x7FFFFFFF here. I think that is some weird
* compiler optimization in the original driver.
* Essentially, what we do here is resetting all NRSSI LT
* entries to -32 (see the limit_value() in nrssi_hw_update())
*/
bcm43xx_nrssi_hw_update(bcm, 0xFFFF);
bcm43xx_calc_nrssi_threshold(bcm);
} else if (phy->connected) {
if (radio->nrssi[0] == -1000) {
assert(radio->nrssi[1] == -1000);
bcm43xx_calc_nrssi_slope(bcm);
} else {
assert(radio->nrssi[1] != -1000);
bcm43xx_calc_nrssi_threshold(bcm);
}
}
if (radio->revision == 8)
bcm43xx_phy_write(bcm, 0x0805, 0x3230);
bcm43xx_phy_init_pctl(bcm);
if (bcm->chip_id == 0x4306 && bcm->chip_package == 2) {
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF);
bcm43xx_phy_write(bcm, 0x04C3,
bcm43xx_phy_read(bcm, 0x04C3) & 0x7FFF);
}
}
static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm)
{
int i;
u16 ret = 0;
unsigned long flags;
local_irq_save(flags);
for (i = 0; i < 10; i++){
bcm43xx_phy_write(bcm, 0x0015, 0xAFA0);
udelay(1);
bcm43xx_phy_write(bcm, 0x0015, 0xEFA0);
udelay(10);
bcm43xx_phy_write(bcm, 0x0015, 0xFFA0);
udelay(40);
ret += bcm43xx_phy_read(bcm, 0x002C);
}
local_irq_restore(flags);
bcm43xx_voluntary_preempt();
return ret;
}
void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 regstack[12] = { 0 };
u16 mls;
u16 fval;
int i, j;
regstack[0] = bcm43xx_phy_read(bcm, 0x0015);
regstack[1] = bcm43xx_radio_read16(bcm, 0x0052) & 0xFFF0;
if (radio->version == 0x2053) {
regstack[2] = bcm43xx_phy_read(bcm, 0x000A);
regstack[3] = bcm43xx_phy_read(bcm, 0x002A);
regstack[4] = bcm43xx_phy_read(bcm, 0x0035);
regstack[5] = bcm43xx_phy_read(bcm, 0x0003);
regstack[6] = bcm43xx_phy_read(bcm, 0x0001);
regstack[7] = bcm43xx_phy_read(bcm, 0x0030);
regstack[8] = bcm43xx_radio_read16(bcm, 0x0043);
regstack[9] = bcm43xx_radio_read16(bcm, 0x007A);
regstack[10] = bcm43xx_read16(bcm, 0x03EC);
regstack[11] = bcm43xx_radio_read16(bcm, 0x0052) & 0x00F0;
bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
bcm43xx_write16(bcm, 0x03EC, 0x3F3F);
bcm43xx_phy_write(bcm, 0x0035, regstack[4] & 0xFF7F);
bcm43xx_radio_write16(bcm, 0x007A, regstack[9] & 0xFFF0);
}
bcm43xx_phy_write(bcm, 0x0015, 0xB000);
bcm43xx_phy_write(bcm, 0x002B, 0x0004);
if (radio->version == 0x2053) {
bcm43xx_phy_write(bcm, 0x002B, 0x0203);
bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
}
phy->minlowsig[0] = 0xFFFF;
for (i = 0; i < 4; i++) {
bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i);
bcm43xx_phy_lo_b_r15_loop(bcm);
}
for (i = 0; i < 10; i++) {
bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i);
mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10;
if (mls < phy->minlowsig[0]) {
phy->minlowsig[0] = mls;
phy->minlowsigpos[0] = i;
}
}
bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | phy->minlowsigpos[0]);
phy->minlowsig[1] = 0xFFFF;
for (i = -4; i < 5; i += 2) {
for (j = -4; j < 5; j += 2) {
if (j < 0)
fval = (0x0100 * i) + j + 0x0100;
else
fval = (0x0100 * i) + j;
bcm43xx_phy_write(bcm, 0x002F, fval);
mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10;
if (mls < phy->minlowsig[1]) {
phy->minlowsig[1] = mls;
phy->minlowsigpos[1] = fval;
}
}
}
phy->minlowsigpos[1] += 0x0101;
bcm43xx_phy_write(bcm, 0x002F, phy->minlowsigpos[1]);
if (radio->version == 0x2053) {
bcm43xx_phy_write(bcm, 0x000A, regstack[2]);
bcm43xx_phy_write(bcm, 0x002A, regstack[3]);
bcm43xx_phy_write(bcm, 0x0035, regstack[4]);
bcm43xx_phy_write(bcm, 0x0003, regstack[5]);
bcm43xx_phy_write(bcm, 0x0001, regstack[6]);
bcm43xx_phy_write(bcm, 0x0030, regstack[7]);
bcm43xx_radio_write16(bcm, 0x0043, regstack[8]);
bcm43xx_radio_write16(bcm, 0x007A, regstack[9]);
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052) & 0x000F)
| regstack[11]);
bcm43xx_write16(bcm, 0x03EC, regstack[10]);
}
bcm43xx_phy_write(bcm, 0x0015, regstack[0]);
}
static inline
u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 ret;
unsigned long flags;
local_irq_save(flags);
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x15, 0xE300);
control <<= 8;
bcm43xx_phy_write(bcm, 0x0812, control | 0x00B0);
udelay(5);
bcm43xx_phy_write(bcm, 0x0812, control | 0x00B2);
udelay(2);
bcm43xx_phy_write(bcm, 0x0812, control | 0x00B3);
udelay(4);
bcm43xx_phy_write(bcm, 0x0015, 0xF300);
udelay(8);
} else {
bcm43xx_phy_write(bcm, 0x0015, control | 0xEFA0);
udelay(2);
bcm43xx_phy_write(bcm, 0x0015, control | 0xEFE0);
udelay(4);
bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0);
udelay(8);
}
ret = bcm43xx_phy_read(bcm, 0x002D);
local_irq_restore(flags);
bcm43xx_voluntary_preempt();
return ret;
}
static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control)
{
int i;
u32 ret = 0;
for (i = 0; i < 8; i++)
ret += bcm43xx_phy_lo_g_deviation_subval(bcm, control);
return ret;
}
/* Write the LocalOscillator CONTROL */
static inline
void bcm43xx_lo_write(struct bcm43xx_private *bcm,
struct bcm43xx_lopair *pair)
{
u16 value;
value = (u8)(pair->low);
value |= ((u8)(pair->high)) << 8;
#ifdef CONFIG_BCM43XX_DEBUG
/* Sanity check. */
if (pair->low < -8 || pair->low > 8 ||
pair->high < -8 || pair->high > 8) {
printk(KERN_WARNING PFX
"WARNING: Writing invalid LOpair "
"(low: %d, high: %d, index: %lu)\n",
pair->low, pair->high,
(unsigned long)(pair - bcm43xx_current_phy(bcm)->_lo_pairs));
dump_stack();
}
#endif
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, value);
}
static inline
struct bcm43xx_lopair * bcm43xx_find_lopair(struct bcm43xx_private *bcm,
u16 baseband_attenuation,
u16 radio_attenuation,
u16 tx)
{
static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 };
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
if (baseband_attenuation > 6)
baseband_attenuation = 6;
assert(radio_attenuation < 10);
if (tx == 3) {
return bcm43xx_get_lopair(phy,
radio_attenuation,
baseband_attenuation);
}
return bcm43xx_get_lopair(phy, dict[radio_attenuation], baseband_attenuation);
}
static inline
struct bcm43xx_lopair * bcm43xx_current_lopair(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
return bcm43xx_find_lopair(bcm,
radio->baseband_atten,
radio->radio_atten,
radio->txctl1);
}
/* Adjust B/G LO */
void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed)
{
struct bcm43xx_lopair *pair;
if (fixed) {
/* Use fixed values. Only for initialization. */
pair = bcm43xx_find_lopair(bcm, 2, 3, 0);
} else
pair = bcm43xx_current_lopair(bcm);
bcm43xx_lo_write(bcm, pair);
}
static void bcm43xx_phy_lo_g_measure_txctl2(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 txctl2 = 0, i;
u32 smallest, tmp;
bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
udelay(10);
smallest = bcm43xx_phy_lo_g_singledeviation(bcm, 0);
for (i = 0; i < 16; i++) {
bcm43xx_radio_write16(bcm, 0x0052, i);
udelay(10);
tmp = bcm43xx_phy_lo_g_singledeviation(bcm, 0);
if (tmp < smallest) {
smallest = tmp;
txctl2 = i;
}
}
radio->txctl2 = txctl2;
}
static
void bcm43xx_phy_lo_g_state(struct bcm43xx_private *bcm,
const struct bcm43xx_lopair *in_pair,
struct bcm43xx_lopair *out_pair,
u16 r27)
{
static const struct bcm43xx_lopair transitions[8] = {
{ .high = 1, .low = 1, },
{ .high = 1, .low = 0, },
{ .high = 1, .low = -1, },
{ .high = 0, .low = -1, },
{ .high = -1, .low = -1, },
{ .high = -1, .low = 0, },
{ .high = -1, .low = 1, },
{ .high = 0, .low = 1, },
};
struct bcm43xx_lopair lowest_transition = {
.high = in_pair->high,
.low = in_pair->low,
};
struct bcm43xx_lopair tmp_pair;
struct bcm43xx_lopair transition;
int i = 12;
int state = 0;
int found_lower;
int j, begin, end;
u32 lowest_deviation;
u32 tmp;
/* Note that in_pair and out_pair can point to the same pair. Be careful. */
bcm43xx_lo_write(bcm, &lowest_transition);
lowest_deviation = bcm43xx_phy_lo_g_singledeviation(bcm, r27);
do {
found_lower = 0;
assert(state >= 0 && state <= 8);
if (state == 0) {
begin = 1;
end = 8;
} else if (state % 2 == 0) {
begin = state - 1;
end = state + 1;
} else {
begin = state - 2;
end = state + 2;
}
if (begin < 1)
begin += 8;
if (end > 8)
end -= 8;
j = begin;
tmp_pair.high = lowest_transition.high;
tmp_pair.low = lowest_transition.low;
while (1) {
assert(j >= 1 && j <= 8);
transition.high = tmp_pair.high + transitions[j - 1].high;
transition.low = tmp_pair.low + transitions[j - 1].low;
if ((abs(transition.low) < 9) && (abs(transition.high) < 9)) {
bcm43xx_lo_write(bcm, &transition);
tmp = bcm43xx_phy_lo_g_singledeviation(bcm, r27);
if (tmp < lowest_deviation) {
lowest_deviation = tmp;
state = j;
found_lower = 1;
lowest_transition.high = transition.high;
lowest_transition.low = transition.low;
}
}
if (j == end)
break;
if (j == 8)
j = 1;
else
j++;
}
} while (i-- && found_lower);
out_pair->high = lowest_transition.high;
out_pair->low = lowest_transition.low;
}
/* Set the baseband attenuation value on chip. */
void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm,
u16 baseband_attenuation)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 value;
if (phy->analog == 0) {
value = (bcm43xx_read16(bcm, 0x03E6) & 0xFFF0);
value |= (baseband_attenuation & 0x000F);
bcm43xx_write16(bcm, 0x03E6, value);
return;
}
if (phy->analog > 1) {
value = bcm43xx_phy_read(bcm, 0x0060) & ~0x003C;
value |= (baseband_attenuation << 2) & 0x003C;
} else {
value = bcm43xx_phy_read(bcm, 0x0060) & ~0x0078;
value |= (baseband_attenuation << 3) & 0x0078;
}
bcm43xx_phy_write(bcm, 0x0060, value);
}
/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */
void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm)
{
static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 };
const int is_initializing = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZING);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 h, i, oldi = 0, j;
struct bcm43xx_lopair control;
struct bcm43xx_lopair *tmp_control;
u16 tmp;
u16 regstack[16] = { 0 };
u8 oldchannel;
//XXX: What are these?
u8 r27 = 0, r31;
oldchannel = radio->channel;
/* Setup */
if (phy->connected) {
regstack[0] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS);
regstack[1] = bcm43xx_phy_read(bcm, 0x0802);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF);
bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC);
}
regstack[3] = bcm43xx_read16(bcm, 0x03E2);
bcm43xx_write16(bcm, 0x03E2, regstack[3] | 0x8000);
regstack[4] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
regstack[5] = bcm43xx_phy_read(bcm, 0x15);
regstack[6] = bcm43xx_phy_read(bcm, 0x2A);
regstack[7] = bcm43xx_phy_read(bcm, 0x35);
regstack[8] = bcm43xx_phy_read(bcm, 0x60);
regstack[9] = bcm43xx_radio_read16(bcm, 0x43);
regstack[10] = bcm43xx_radio_read16(bcm, 0x7A);
regstack[11] = bcm43xx_radio_read16(bcm, 0x52);
if (phy->connected) {
regstack[12] = bcm43xx_phy_read(bcm, 0x0811);
regstack[13] = bcm43xx_phy_read(bcm, 0x0812);
regstack[14] = bcm43xx_phy_read(bcm, 0x0814);
regstack[15] = bcm43xx_phy_read(bcm, 0x0815);
}
bcm43xx_radio_selectchannel(bcm, 6, 0);
if (phy->connected) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF);
bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC);
bcm43xx_dummy_transmission(bcm);
}
bcm43xx_radio_write16(bcm, 0x0043, 0x0006);
bcm43xx_phy_set_baseband_attenuation(bcm, 2);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x0000);
bcm43xx_phy_write(bcm, 0x002E, 0x007F);
bcm43xx_phy_write(bcm, 0x080F, 0x0078);
bcm43xx_phy_write(bcm, 0x0035, regstack[7] & ~(1 << 7));
bcm43xx_radio_write16(bcm, 0x007A, regstack[10] & 0xFFF0);
bcm43xx_phy_write(bcm, 0x002B, 0x0203);
bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x0814, regstack[14] | 0x0003);
bcm43xx_phy_write(bcm, 0x0815, regstack[15] & 0xFFFC);
bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
bcm43xx_phy_write(bcm, 0x0812, 0x00B2);
}
if (is_initializing)
bcm43xx_phy_lo_g_measure_txctl2(bcm);
bcm43xx_phy_write(bcm, 0x080F, 0x8078);
/* Measure */
control.low = 0;
control.high = 0;
for (h = 0; h < 10; h++) {
/* Loop over each possible RadioAttenuation (0-9) */
i = pairorder[h];
if (is_initializing) {
if (i == 3) {
control.low = 0;
control.high = 0;
} else if (((i % 2 == 1) && (oldi % 2 == 1)) ||
((i % 2 == 0) && (oldi % 2 == 0))) {
tmp_control = bcm43xx_get_lopair(phy, oldi, 0);
memcpy(&control, tmp_control, sizeof(control));
} else {
tmp_control = bcm43xx_get_lopair(phy, 3, 0);
memcpy(&control, tmp_control, sizeof(control));
}
}
/* Loop over each possible BasebandAttenuation/2 */
for (j = 0; j < 4; j++) {
if (is_initializing) {
tmp = i * 2 + j;
r27 = 0;
r31 = 0;
if (tmp > 14) {
r31 = 1;
if (tmp > 17)
r27 = 1;
if (tmp > 19)
r27 = 2;
}
} else {
tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
if (!tmp_control->used)
continue;
memcpy(&control, tmp_control, sizeof(control));
r27 = 3;
r31 = 0;
}
bcm43xx_radio_write16(bcm, 0x43, i);
bcm43xx_radio_write16(bcm, 0x52, radio->txctl2);
udelay(10);
bcm43xx_voluntary_preempt();
bcm43xx_phy_set_baseband_attenuation(bcm, j * 2);
tmp = (regstack[10] & 0xFFF0);
if (r31)
tmp |= 0x0008;
bcm43xx_radio_write16(bcm, 0x007A, tmp);
tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27);
}
oldi = i;
}
/* Loop over each possible RadioAttenuation (10-13) */
for (i = 10; i < 14; i++) {
/* Loop over each possible BasebandAttenuation/2 */
for (j = 0; j < 4; j++) {
if (is_initializing) {
tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2);
memcpy(&control, tmp_control, sizeof(control));
tmp = (i - 9) * 2 + j - 5;//FIXME: This is wrong, as the following if statement can never trigger.
r27 = 0;
r31 = 0;
if (tmp > 14) {
r31 = 1;
if (tmp > 17)
r27 = 1;
if (tmp > 19)
r27 = 2;
}
} else {
tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2);
if (!tmp_control->used)
continue;
memcpy(&control, tmp_control, sizeof(control));
r27 = 3;
r31 = 0;
}
bcm43xx_radio_write16(bcm, 0x43, i - 9);
bcm43xx_radio_write16(bcm, 0x52,
radio->txctl2
| (3/*txctl1*/ << 4));//FIXME: shouldn't txctl1 be zero here and 3 in the loop above?
udelay(10);
bcm43xx_voluntary_preempt();
bcm43xx_phy_set_baseband_attenuation(bcm, j * 2);
tmp = (regstack[10] & 0xFFF0);
if (r31)
tmp |= 0x0008;
bcm43xx_radio_write16(bcm, 0x7A, tmp);
tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27);
}
}
/* Restoration */
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x0015, 0xE300);
bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA0);
udelay(5);
bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA2);
udelay(2);
bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA3);
bcm43xx_voluntary_preempt();
} else
bcm43xx_phy_write(bcm, 0x0015, r27 | 0xEFA0);
bcm43xx_phy_lo_adjust(bcm, is_initializing);
bcm43xx_phy_write(bcm, 0x002E, 0x807F);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x002F, 0x0202);
else
bcm43xx_phy_write(bcm, 0x002F, 0x0101);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, regstack[4]);
bcm43xx_phy_write(bcm, 0x0015, regstack[5]);
bcm43xx_phy_write(bcm, 0x002A, regstack[6]);
bcm43xx_phy_write(bcm, 0x0035, regstack[7]);
bcm43xx_phy_write(bcm, 0x0060, regstack[8]);
bcm43xx_radio_write16(bcm, 0x0043, regstack[9]);
bcm43xx_radio_write16(bcm, 0x007A, regstack[10]);
regstack[11] &= 0x00F0;
regstack[11] |= (bcm43xx_radio_read16(bcm, 0x52) & 0x000F);
bcm43xx_radio_write16(bcm, 0x52, regstack[11]);
bcm43xx_write16(bcm, 0x03E2, regstack[3]);
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x0811, regstack[12]);
bcm43xx_phy_write(bcm, 0x0812, regstack[13]);
bcm43xx_phy_write(bcm, 0x0814, regstack[14]);
bcm43xx_phy_write(bcm, 0x0815, regstack[15]);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0]);
bcm43xx_phy_write(bcm, 0x0802, regstack[1]);
}
bcm43xx_radio_selectchannel(bcm, oldchannel, 1);
#ifdef CONFIG_BCM43XX_DEBUG
{
/* Sanity check for all lopairs. */
for (i = 0; i < BCM43xx_LO_COUNT; i++) {
tmp_control = phy->_lo_pairs + i;
if (tmp_control->low < -8 || tmp_control->low > 8 ||
tmp_control->high < -8 || tmp_control->high > 8) {
printk(KERN_WARNING PFX
"WARNING: Invalid LOpair (low: %d, high: %d, index: %d)\n",
tmp_control->low, tmp_control->high, i);
}
}
}
#endif /* CONFIG_BCM43XX_DEBUG */
}
static
void bcm43xx_phy_lo_mark_current_used(struct bcm43xx_private *bcm)
{
struct bcm43xx_lopair *pair;
pair = bcm43xx_current_lopair(bcm);
pair->used = 1;
}
void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_lopair *pair;
int i;
for (i = 0; i < BCM43xx_LO_COUNT; i++) {
pair = phy->_lo_pairs + i;
pair->used = 0;
}
}
/* http://bcm-specs.sipsolutions.net/EstimatePowerOut
* This function converts a TSSI value to dBm in Q5.2
*/
static s8 bcm43xx_phy_estimate_power_out(struct bcm43xx_private *bcm, s8 tssi)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
s8 dbm = 0;
s32 tmp;
tmp = phy->idle_tssi;
tmp += tssi;
tmp -= phy->savedpctlreg;
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
tmp += 0x80;
tmp = limit_value(tmp, 0x00, 0xFF);
dbm = phy->tssi2dbm[tmp];
TODO(); //TODO: There's a FIXME on the specs
break;
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G:
tmp = limit_value(tmp, 0x00, 0x3F);
dbm = phy->tssi2dbm[tmp];
break;
default:
assert(0);
}
return dbm;
}
/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
if (phy->savedpctlreg == 0xFFFF)
return;
if ((bcm->board_type == 0x0416) &&
(bcm->board_vendor == PCI_VENDOR_ID_BROADCOM))
return;
switch (phy->type) {
case BCM43xx_PHYTYPE_A: {
TODO(); //TODO: Nothing for A PHYs yet :-/
break;
}
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G: {
u16 tmp;
u16 txpower;
s8 v0, v1, v2, v3;
s8 average;
u8 max_pwr;
s16 desired_pwr, estimated_pwr, pwr_adjust;
s16 radio_att_delta, baseband_att_delta;
s16 radio_attenuation, baseband_attenuation;
unsigned long phylock_flags;
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0058);
v0 = (s8)(tmp & 0x00FF);
v1 = (s8)((tmp & 0xFF00) >> 8);
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005A);
v2 = (s8)(tmp & 0x00FF);
v3 = (s8)((tmp & 0xFF00) >> 8);
tmp = 0;
if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) {
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0070);
v0 = (s8)(tmp & 0x00FF);
v1 = (s8)((tmp & 0xFF00) >> 8);
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0072);
v2 = (s8)(tmp & 0x00FF);
v3 = (s8)((tmp & 0xFF00) >> 8);
if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F)
return;
v0 = (v0 + 0x20) & 0x3F;
v1 = (v1 + 0x20) & 0x3F;
v2 = (v2 + 0x20) & 0x3F;
v3 = (v3 + 0x20) & 0x3F;
tmp = 1;
}
bcm43xx_radio_clear_tssi(bcm);
average = (v0 + v1 + v2 + v3 + 2) / 4;
if (tmp && (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005E) & 0x8))
average -= 13;
estimated_pwr = bcm43xx_phy_estimate_power_out(bcm, average);
max_pwr = bcm->sprom.maxpower_bgphy;
if ((bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) &&
(phy->type == BCM43xx_PHYTYPE_G))
max_pwr -= 0x3;
/*TODO:
max_pwr = min(REG - bcm->sprom.antennagain_bgphy - 0x6, max_pwr)
where REG is the max power as per the regulatory domain
*/
desired_pwr = limit_value(radio->txpower_desired, 0, max_pwr);
/* Check if we need to adjust the current power. */
pwr_adjust = desired_pwr - estimated_pwr;
radio_att_delta = -(pwr_adjust + 7) >> 3;
baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta);
if ((radio_att_delta == 0) && (baseband_att_delta == 0)) {
bcm43xx_phy_lo_mark_current_used(bcm);
return;
}
/* Calculate the new attenuation values. */
baseband_attenuation = radio->baseband_atten;
baseband_attenuation += baseband_att_delta;
radio_attenuation = radio->radio_atten;
radio_attenuation += radio_att_delta;
/* Get baseband and radio attenuation values into their permitted ranges.
* baseband 0-11, radio 0-9.
* Radio attenuation affects power level 4 times as much as baseband.
*/
if (radio_attenuation < 0) {
baseband_attenuation -= (4 * -radio_attenuation);
radio_attenuation = 0;
} else if (radio_attenuation > 9) {
baseband_attenuation += (4 * (radio_attenuation - 9));
radio_attenuation = 9;
} else {
while (baseband_attenuation < 0 && radio_attenuation > 0) {
baseband_attenuation += 4;
radio_attenuation--;
}
while (baseband_attenuation > 11 && radio_attenuation < 9) {
baseband_attenuation -= 4;
radio_attenuation++;
}
}
baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
txpower = radio->txctl1;
if ((radio->version == 0x2050) && (radio->revision == 2)) {
if (radio_attenuation <= 1) {
if (txpower == 0) {
txpower = 3;
radio_attenuation += 2;
baseband_attenuation += 2;
} else if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) {
baseband_attenuation += 4 * (radio_attenuation - 2);
radio_attenuation = 2;
}
} else if (radio_attenuation > 4 && txpower != 0) {
txpower = 0;
if (baseband_attenuation < 3) {
radio_attenuation -= 3;
baseband_attenuation += 2;
} else {
radio_attenuation -= 2;
baseband_attenuation -= 2;
}
}
}
radio->txctl1 = txpower;
baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
radio_attenuation = limit_value(radio_attenuation, 0, 9);
bcm43xx_phy_lock(bcm, phylock_flags);
bcm43xx_radio_lock(bcm);
bcm43xx_radio_set_txpower_bg(bcm, baseband_attenuation,
radio_attenuation, txpower);
bcm43xx_phy_lo_mark_current_used(bcm);
bcm43xx_radio_unlock(bcm);
bcm43xx_phy_unlock(bcm, phylock_flags);
break;
}
default:
assert(0);
}
}
static inline
s32 bcm43xx_tssi2dbm_ad(s32 num, s32 den)
{
if (num < 0)
return num/den;
else
return (num+den/2)/den;
}
static inline
s8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2)
{
s32 m1, m2, f = 256, q, delta;
s8 i = 0;
m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32);
m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1);
do {
if (i > 15)
return -EINVAL;
q = bcm43xx_tssi2dbm_ad(f * 4096 -
bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048);
delta = abs(q - f);
f = q;
i++;
} while (delta >= 2);
entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128);
return 0;
}
/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */
int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
s16 pab0, pab1, pab2;
u8 idx;
s8 *dyn_tssi2dbm;
if (phy->type == BCM43xx_PHYTYPE_A) {
pab0 = (s16)(bcm->sprom.pa1b0);
pab1 = (s16)(bcm->sprom.pa1b1);
pab2 = (s16)(bcm->sprom.pa1b2);
} else {
pab0 = (s16)(bcm->sprom.pa0b0);
pab1 = (s16)(bcm->sprom.pa0b1);
pab2 = (s16)(bcm->sprom.pa0b2);
}
if ((bcm->chip_id == 0x4301) && (radio->version != 0x2050)) {
phy->idle_tssi = 0x34;
phy->tssi2dbm = bcm43xx_tssi2dbm_b_table;
return 0;
}
if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
pab0 != -1 && pab1 != -1 && pab2 != -1) {
/* The pabX values are set in SPROM. Use them. */
if (phy->type == BCM43xx_PHYTYPE_A) {
if ((s8)bcm->sprom.idle_tssi_tgt_aphy != 0 &&
(s8)bcm->sprom.idle_tssi_tgt_aphy != -1)
phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_aphy);
else
phy->idle_tssi = 62;
} else {
if ((s8)bcm->sprom.idle_tssi_tgt_bgphy != 0 &&
(s8)bcm->sprom.idle_tssi_tgt_bgphy != -1)
phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_bgphy);
else
phy->idle_tssi = 62;
}
dyn_tssi2dbm = kmalloc(64, GFP_KERNEL);
if (dyn_tssi2dbm == NULL) {
printk(KERN_ERR PFX "Could not allocate memory"
"for tssi2dbm table\n");
return -ENOMEM;
}
for (idx = 0; idx < 64; idx++)
if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) {
phy->tssi2dbm = NULL;
printk(KERN_ERR PFX "Could not generate "
"tssi2dBm table\n");
kfree(dyn_tssi2dbm);
return -ENODEV;
}
phy->tssi2dbm = dyn_tssi2dbm;
phy->dyn_tssi_tbl = 1;
} else {
/* pabX values not set in SPROM. */
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
/* APHY needs a generated table. */
phy->tssi2dbm = NULL;
printk(KERN_ERR PFX "Could not generate tssi2dBm "
"table (wrong SPROM info)!\n");
return -ENODEV;
case BCM43xx_PHYTYPE_B:
phy->idle_tssi = 0x34;
phy->tssi2dbm = bcm43xx_tssi2dbm_b_table;
break;
case BCM43xx_PHYTYPE_G:
phy->idle_tssi = 0x34;
phy->tssi2dbm = bcm43xx_tssi2dbm_g_table;
break;
}
}
return 0;
}
int bcm43xx_phy_init(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int err = -ENODEV;
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
if (phy->rev == 2 || phy->rev == 3) {
bcm43xx_phy_inita(bcm);
err = 0;
}
break;
case BCM43xx_PHYTYPE_B:
switch (phy->rev) {
case 2:
bcm43xx_phy_initb2(bcm);
err = 0;
break;
case 4:
bcm43xx_phy_initb4(bcm);
err = 0;
break;
case 5:
bcm43xx_phy_initb5(bcm);
err = 0;
break;
case 6:
bcm43xx_phy_initb6(bcm);
err = 0;
break;
}
break;
case BCM43xx_PHYTYPE_G:
bcm43xx_phy_initg(bcm);
err = 0;
break;
}
if (err)
printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n");
return err;
}
void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 antennadiv;
u16 offset;
u16 value;
u32 ucodeflags;
antennadiv = phy->antenna_diversity;
if (antennadiv == 0xFFFF)
antennadiv = 3;
assert(antennadiv <= 3);
ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET);
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
ucodeflags & ~BCM43xx_UCODEFLAG_AUTODIV);
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
case BCM43xx_PHYTYPE_G:
if (phy->type == BCM43xx_PHYTYPE_A)
offset = 0x0000;
else
offset = 0x0400;
if (antennadiv == 2)
value = (3/*automatic*/ << 7);
else
value = (antennadiv << 7);
bcm43xx_phy_write(bcm, offset + 1,
(bcm43xx_phy_read(bcm, offset + 1)
& 0x7E7F) | value);
if (antennadiv >= 2) {
if (antennadiv == 2)
value = (antennadiv << 7);
else
value = (0/*force0*/ << 7);
bcm43xx_phy_write(bcm, offset + 0x2B,
(bcm43xx_phy_read(bcm, offset + 0x2B)
& 0xFEFF) | value);
}
if (phy->type == BCM43xx_PHYTYPE_G) {
if (antennadiv >= 2)
bcm43xx_phy_write(bcm, 0x048C,
bcm43xx_phy_read(bcm, 0x048C)
| 0x2000);
else
bcm43xx_phy_write(bcm, 0x048C,
bcm43xx_phy_read(bcm, 0x048C)
& ~0x2000);
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0461,
bcm43xx_phy_read(bcm, 0x0461)
| 0x0010);
bcm43xx_phy_write(bcm, 0x04AD,
(bcm43xx_phy_read(bcm, 0x04AD)
& 0x00FF) | 0x0015);
if (phy->rev == 2)
bcm43xx_phy_write(bcm, 0x0427, 0x0008);
else
bcm43xx_phy_write(bcm, 0x0427,
(bcm43xx_phy_read(bcm, 0x0427)
& 0x00FF) | 0x0008);
}
else if (phy->rev >= 6)
bcm43xx_phy_write(bcm, 0x049B, 0x00DC);
} else {
if (phy->rev < 3)
bcm43xx_phy_write(bcm, 0x002B,
(bcm43xx_phy_read(bcm, 0x002B)
& 0x00FF) | 0x0024);
else {
bcm43xx_phy_write(bcm, 0x0061,
bcm43xx_phy_read(bcm, 0x0061)
| 0x0010);
if (phy->rev == 3) {
bcm43xx_phy_write(bcm, 0x0093, 0x001D);
bcm43xx_phy_write(bcm, 0x0027, 0x0008);
} else {
bcm43xx_phy_write(bcm, 0x0093, 0x003A);
bcm43xx_phy_write(bcm, 0x0027,
(bcm43xx_phy_read(bcm, 0x0027)
& 0x00FF) | 0x0008);
}
}
}
break;
case BCM43xx_PHYTYPE_B:
if (bcm->current_core->rev == 2)
value = (3/*automatic*/ << 7);
else
value = (antennadiv << 7);
bcm43xx_phy_write(bcm, 0x03E2,
(bcm43xx_phy_read(bcm, 0x03E2)
& 0xFE7F) | value);
break;
default:
assert(0);
}
if (antennadiv >= 2) {
ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET);
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
ucodeflags | BCM43xx_UCODEFLAG_AUTODIV);
}
phy->antenna_diversity = antennadiv;
}