📄 bcm43xx_radio.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 "bcm43xx.h"#include "bcm43xx_main.h"#include "bcm43xx_phy.h"#include "bcm43xx_radio.h"#include "bcm43xx_ilt.h"/* Table for bcm43xx_radio_calibrationvalue() */static const u16 rcc_table[16] = { 0x0002, 0x0003, 0x0001, 0x000F, 0x0006, 0x0007, 0x0005, 0x000F, 0x000A, 0x000B, 0x0009, 0x000F, 0x000E, 0x000F, 0x000D, 0x000F,};/* Reverse the bits of a 4bit value. * Example: 1101 is flipped 1011 */static u16 flip_4bit(u16 value){ u16 flipped = 0x0000; assert((value & ~0x000F) == 0x0000); flipped |= (value & 0x0001) << 3; flipped |= (value & 0x0002) << 1; flipped |= (value & 0x0004) >> 1; flipped |= (value & 0x0008) >> 3; return flipped;}/* Get the freq, as it has to be written to the device. */static inlineu16 channel2freq_bg(u8 channel){ /* Frequencies are given as frequencies_bg[index] + 2.4GHz * Starting with channel 1 */ static const u16 frequencies_bg[14] = { 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 84, }; assert(channel >= 1 && channel <= 14); return frequencies_bg[channel - 1];}/* Get the freq, as it has to be written to the device. */static inlineu16 channel2freq_a(u8 channel){ assert(channel <= 200); return (5000 + 5 * channel);}void bcm43xx_radio_lock(struct bcm43xx_private *bcm){ u32 status; status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); status |= BCM43xx_SBF_RADIOREG_LOCK; bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); mmiowb(); udelay(10);}void bcm43xx_radio_unlock(struct bcm43xx_private *bcm){ u32 status; bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); /* dummy read */ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); status &= ~BCM43xx_SBF_RADIOREG_LOCK; bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); mmiowb();}u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset){ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); switch (phy->type) { case BCM43xx_PHYTYPE_A: offset |= 0x0040; break; case BCM43xx_PHYTYPE_B: if (radio->version == 0x2053) { if (offset < 0x70) offset += 0x80; else if (offset < 0x80) offset += 0x70; } else if (radio->version == 0x2050) { offset |= 0x80; } else assert(0); break; case BCM43xx_PHYTYPE_G: offset |= 0x80; break; } bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset); return bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW);}void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val){ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset); mmiowb(); bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW, val);}static void bcm43xx_set_all_gains(struct bcm43xx_private *bcm, s16 first, s16 second, s16 third){ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); u16 i; u16 start = 0x08, end = 0x18; u16 offset = 0x0400; u16 tmp; if (phy->rev <= 1) { offset = 0x5000; start = 0x10; end = 0x20; } for (i = 0; i < 4; i++) bcm43xx_ilt_write(bcm, offset + i, first); for (i = start; i < end; i++) bcm43xx_ilt_write(bcm, offset + i, second); if (third != -1) { tmp = ((u16)third << 14) | ((u16)third << 6); bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | tmp); bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | tmp); bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | tmp); } bcm43xx_dummy_transmission(bcm);}static void bcm43xx_set_original_gains(struct bcm43xx_private *bcm){ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); u16 i, tmp; u16 offset = 0x0400; u16 start = 0x0008, end = 0x0018; if (phy->rev <= 1) { offset = 0x5000; start = 0x0010; end = 0x0020; } for (i = 0; i < 4; i++) { tmp = (i & 0xFFFC); tmp |= (i & 0x0001) << 1; tmp |= (i & 0x0002) >> 1; bcm43xx_ilt_write(bcm, offset + i, tmp); } for (i = start; i < end; i++) bcm43xx_ilt_write(bcm, offset + i, i - start); bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | 0x4040); bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | 0x4040); bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | 0x4000); bcm43xx_dummy_transmission(bcm);}/* Synthetic PU workaround */static void bcm43xx_synth_pu_workaround(struct bcm43xx_private *bcm, u8 channel){ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); if (radio->version != 0x2050 || radio->revision >= 6) { /* We do not need the workaround. */ return; } if (channel <= 10) { bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, channel2freq_bg(channel + 4)); } else { bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, channel2freq_bg(1)); } udelay(100); bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, channel2freq_bg(channel));}u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel){ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); u8 ret = 0; u16 saved, rssi, temp; int i, j = 0; saved = bcm43xx_phy_read(bcm, 0x0403); bcm43xx_radio_selectchannel(bcm, channel, 0); bcm43xx_phy_write(bcm, 0x0403, (saved & 0xFFF8) | 5); if (radio->aci_hw_rssi) rssi = bcm43xx_phy_read(bcm, 0x048A) & 0x3F; else rssi = saved & 0x3F; /* clamp temp to signed 5bit */ if (rssi > 32) rssi -= 64; for (i = 0;i < 100; i++) { temp = (bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x3F; if (temp > 32) temp -= 64; if (temp < rssi) j++; if (j >= 20) ret = 1; } bcm43xx_phy_write(bcm, 0x0403, saved); return ret;}u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm){ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); u8 ret[13]; unsigned int channel = radio->channel; unsigned int i, j, start, end; unsigned long phylock_flags; if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0))) return 0; bcm43xx_phy_lock(bcm, phylock_flags); bcm43xx_radio_lock(bcm); bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF); bcm43xx_set_all_gains(bcm, 3, 8, 1); start = (channel - 5 > 0) ? channel - 5 : 1; end = (channel + 5 < 14) ? channel + 5 : 13; for (i = start; i <= end; i++) { if (abs(channel - i) > 2) ret[i-1] = bcm43xx_radio_aci_detect(bcm, i); } bcm43xx_radio_selectchannel(bcm, channel, 0); bcm43xx_phy_write(bcm, 0x0802, (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC) | 0x0003); bcm43xx_phy_write(bcm, 0x0403, bcm43xx_phy_read(bcm, 0x0403) & 0xFFF8); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000); bcm43xx_set_original_gains(bcm); for (i = 0; i < 13; i++) { if (!ret[i]) continue; end = (i + 5 < 13) ? i + 5 : 13; for (j = i; j < end; j++) ret[j] = 1; } bcm43xx_radio_unlock(bcm); bcm43xx_phy_unlock(bcm, phylock_flags); return ret[channel - 1];}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val){ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset); mmiowb(); bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_DATA, (u16)val);}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset){ u16 val; bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset); val = bcm43xx_phy_read(bcm, BCM43xx_PHY_NRSSILT_DATA); return (s16)val;}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val){ u16 i; s16 tmp; for (i = 0; i < 64; i++) { tmp = bcm43xx_nrssi_hw_read(bcm, i); tmp -= val; tmp = limit_value(tmp, -32, 31); bcm43xx_nrssi_hw_write(bcm, i, tmp); }}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm){ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); s16 i, delta; s32 tmp; delta = 0x1F - radio->nrssi[0]; for (i = 0; i < 64; i++) { tmp = (i - delta) * radio->nrssislope; tmp /= 0x10000; tmp += 0x3A; tmp = limit_value(tmp, 0, 0x3F); radio->nrssi_lt[i] = tmp; }}static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm){ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); u16 backup[20] = { 0 }; s16 v47F; u16 i; u16 saved = 0xFFFF; backup[0] = bcm43xx_phy_read(bcm, 0x0001); backup[1] = bcm43xx_phy_read(bcm, 0x0811); backup[2] = bcm43xx_phy_read(bcm, 0x0812); backup[3] = bcm43xx_phy_read(bcm, 0x0814); backup[4] = bcm43xx_phy_read(bcm, 0x0815); backup[5] = bcm43xx_phy_read(bcm, 0x005A); backup[6] = bcm43xx_phy_read(bcm, 0x0059); backup[7] = bcm43xx_phy_read(bcm, 0x0058); backup[8] = bcm43xx_phy_read(bcm, 0x000A); backup[9] = bcm43xx_phy_read(bcm, 0x0003); backup[10] = bcm43xx_radio_read16(bcm, 0x007A); backup[11] = bcm43xx_radio_read16(bcm, 0x0043); bcm43xx_phy_write(bcm, 0x0429, bcm43xx_phy_read(bcm, 0x0429) & 0x7FFF); bcm43xx_phy_write(bcm, 0x0001, (bcm43xx_phy_read(bcm, 0x0001) & 0x3FFF) | 0x4000); bcm43xx_phy_write(bcm, 0x0811, bcm43xx_phy_read(bcm, 0x0811) | 0x000C); bcm43xx_phy_write(bcm, 0x0812, (bcm43xx_phy_read(bcm, 0x0812) & 0xFFF3) | 0x0004); bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) & ~(0x1 | 0x2)); if (phy->rev >= 6) { backup[12] = bcm43xx_phy_read(bcm, 0x002E); backup[13] = bcm43xx_phy_read(bcm, 0x002F); backup[14] = bcm43xx_phy_read(bcm, 0x080F); backup[15] = bcm43xx_phy_read(bcm, 0x0810); backup[16] = bcm43xx_phy_read(bcm, 0x0801); backup[17] = bcm43xx_phy_read(bcm, 0x0060); backup[18] = bcm43xx_phy_read(bcm, 0x0014); backup[19] = bcm43xx_phy_read(bcm, 0x0478); bcm43xx_phy_write(bcm, 0x002E, 0); bcm43xx_phy_write(bcm, 0x002F, 0); bcm43xx_phy_write(bcm, 0x080F, 0); bcm43xx_phy_write(bcm, 0x0810, 0); bcm43xx_phy_write(bcm, 0x0478, bcm43xx_phy_read(bcm, 0x0478) | 0x0100); bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0040); bcm43xx_phy_write(bcm, 0x0060, bcm43xx_phy_read(bcm, 0x0060) | 0x0040); bcm43xx_phy_write(bcm, 0x0014, bcm43xx_phy_read(bcm, 0x0014) | 0x0200); } bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0070); bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); udelay(30); v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == 31) { for (i = 7; i >= 4; i--) { bcm43xx_radio_write16(bcm, 0x007B, i); udelay(20); v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F < 31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 4; } else { bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); 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, 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) | 0x0030); bcm43xx_phy_write(bcm, 0x0812, bcm43xx_phy_read(bcm, 0x0812) | 0x0030); bcm43xx_phy_write(bcm, 0x005A, 0x0480); bcm43xx_phy_write(bcm, 0x0059, 0x0810); 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); bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x000F); bcm43xx_set_all_gains(bcm, 3, 0, 1); bcm43xx_radio_write16(bcm, 0x0043, (bcm43xx_radio_read16(bcm, 0x0043) & 0x00F0) | 0x000F); udelay(30); v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == -32) { for (i = 0; i < 4; i++) { bcm43xx_radio_write16(bcm, 0x007B, i); udelay(20); v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F > -31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 3; } else saved = 0; } bcm43xx_radio_write16(bcm, 0x007B, saved); if (phy->rev >= 6) { bcm43xx_phy_write(bcm, 0x002E, backup[12]); bcm43xx_phy_write(bcm, 0x002F, backup[13]); bcm43xx_phy_write(bcm, 0x080F, backup[14]); bcm43xx_phy_write(bcm, 0x0810, backup[15]); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -