Commit 75388acd authored by Larry Finger's avatar Larry Finger Committed by David S. Miller

[B43LEGACY]: add mac80211-based driver for legacy BCM43xx devices

Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e4d6b795
......@@ -804,6 +804,13 @@ L: linux-wireless@vger.kernel.org
W: http://bcm43xx.berlios.de/
S: Maintained
B43LEGACY WIRELESS DRIVER
P: Larry Finger
M: Larry.Finger@lwfinger.net
L: linux-wireless@vger.kernel.org
W: http://bcm43xx.berlios.de/
S: Maintained
BCM43XX WIRELESS DRIVER (SOFTMAC BASED VERSION)
P: Larry Finger
M: Larry.Finger@lwfinger.net
......
......@@ -580,6 +580,7 @@ config ADM8211
source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/bcm43xx/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
endmenu
......@@ -37,6 +37,7 @@ obj-$(CONFIG_PRISM54) += prism54/
obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_BCM43XX) += bcm43xx/
obj-$(CONFIG_B43) += b43/
obj-$(CONFIG_B43LEGACY) += b43legacy/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
# 16-bit wireless PCMCIA client drivers
......
config B43LEGACY
tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)"
depends on SSB_POSSIBLE && MAC80211 && WLAN_80211
select SSB
select FW_LOADER
select HW_RANDOM
---help---
b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and
BCM4303) and early model 802.11g chips (BCM4306 Ver. 2) used in the
Linksys WPC54G V1 PCMCIA devices.
Newer 802.11g and 802.11a devices need b43.
It is safe to include both b43 and b43legacy as the underlying glue
layer will automatically load the correct version for your device.
This driver uses V3 firmware, which must be installed separately using
b43-fwcutter.
This driver can be built as a module (recommended) that will be
called "b43legacy". If unsure, say M.
# Auto-select SSB PCI-HOST support, if possible
config B43LEGACY_PCI_AUTOSELECT
bool
depends on B43LEGACY && SSB_PCIHOST_POSSIBLE
select SSB_PCIHOST
default y
# Auto-select SSB PCICORE driver, if possible
config B43LEGACY_PCICORE_AUTOSELECT
bool
depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE
select SSB_DRIVER_PCICORE
default y
config B43LEGACY_DEBUG
bool "Broadcom 43xx-legacy debugging"
depends on B43LEGACY
default y
---help---
Say Y, because this information will help you get the driver running.
This option generates a minimum of log output.
config B43LEGACY_DMA
bool
depends on B43LEGACY
config B43LEGACY_PIO
bool
depends on B43LEGACY
choice
prompt "Broadcom 43xx-legacy data transfer mode"
depends on B43LEGACY
default B43LEGACY_DMA_AND_PIO_MODE
config B43LEGACY_DMA_AND_PIO_MODE
bool "DMA + PIO"
select B43LEGACY_DMA
select B43LEGACY_PIO
---help---
Include both, Direct Memory Access (DMA) and Programmed I/O (PIO)
data transfer modes. The mode actually used is selectable through
the module parameter "pio". With pio=0 as a module parameter, the
default DMA is used, otherwise PIO is used.
If unsure, choose this option.
config B43LEGACY_DMA_MODE
bool "DMA (Direct Memory Access) only"
select B43LEGACY_DMA
---help---
Only include Direct Memory Access (DMA).
This reduces the size of the driver module, by omitting the PIO code.
config B43LEGACY_PIO_MODE
bool "PIO (Programmed I/O) only"
select B43LEGACY_PIO
---help---
Only include Programmed I/O (PIO).
This reduces the size of the driver module, by omitting the DMA code.
Please note that PIO transfers are slow (compared to DMA).
Also note that not all devices of the b43legacy series support PIO.
You should use PIO only if DMA does not work for you.
endchoice
obj-$(CONFIG_B43LEGACY) += b43legacy.o
b43legacy-obj-$(CONFIG_B43LEGACY_DEBUG) += debugfs.o
b43legacy-obj-$(CONFIG_B43LEGACY_DMA) += dma.o
b43legacy-obj-$(CONFIG_B43LEGACY_PIO) += pio.o
b43legacy-objs := main.o \
ilt.o \
leds.o \
phy.o \
radio.o \
sysfs.o \
xmit.o \
$(b43legacy-obj-y)
#ifndef B43legacy_H_
#define B43legacy_H_
#include <linux/hw_random.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/stringify.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <asm/atomic.h>
#include <linux/io.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_driver_chipcommon.h>
#include <linux/wireless.h>
#include <net/mac80211.h>
#include "debugfs.h"
#include "leds.h"
#include "phy.h"
#define B43legacy_IRQWAIT_MAX_RETRIES 100
#define B43legacy_RX_MAX_SSI 60 /* best guess at max ssi */
/* MMIO offsets */
#define B43legacy_MMIO_DMA0_REASON 0x20
#define B43legacy_MMIO_DMA0_IRQ_MASK 0x24
#define B43legacy_MMIO_DMA1_REASON 0x28
#define B43legacy_MMIO_DMA1_IRQ_MASK 0x2C
#define B43legacy_MMIO_DMA2_REASON 0x30
#define B43legacy_MMIO_DMA2_IRQ_MASK 0x34
#define B43legacy_MMIO_DMA3_REASON 0x38
#define B43legacy_MMIO_DMA3_IRQ_MASK 0x3C
#define B43legacy_MMIO_DMA4_REASON 0x40
#define B43legacy_MMIO_DMA4_IRQ_MASK 0x44
#define B43legacy_MMIO_DMA5_REASON 0x48
#define B43legacy_MMIO_DMA5_IRQ_MASK 0x4C
#define B43legacy_MMIO_MACCTL 0x120
#define B43legacy_MMIO_STATUS_BITFIELD 0x120
#define B43legacy_MMIO_STATUS2_BITFIELD 0x124
#define B43legacy_MMIO_GEN_IRQ_REASON 0x128
#define B43legacy_MMIO_GEN_IRQ_MASK 0x12C
#define B43legacy_MMIO_RAM_CONTROL 0x130
#define B43legacy_MMIO_RAM_DATA 0x134
#define B43legacy_MMIO_PS_STATUS 0x140
#define B43legacy_MMIO_RADIO_HWENABLED_HI 0x158
#define B43legacy_MMIO_SHM_CONTROL 0x160
#define B43legacy_MMIO_SHM_DATA 0x164
#define B43legacy_MMIO_SHM_DATA_UNALIGNED 0x166
#define B43legacy_MMIO_XMITSTAT_0 0x170
#define B43legacy_MMIO_XMITSTAT_1 0x174
#define B43legacy_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */
#define B43legacy_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */
/* 32-bit DMA */
#define B43legacy_MMIO_DMA32_BASE0 0x200
#define B43legacy_MMIO_DMA32_BASE1 0x220
#define B43legacy_MMIO_DMA32_BASE2 0x240
#define B43legacy_MMIO_DMA32_BASE3 0x260
#define B43legacy_MMIO_DMA32_BASE4 0x280
#define B43legacy_MMIO_DMA32_BASE5 0x2A0
/* 64-bit DMA */
#define B43legacy_MMIO_DMA64_BASE0 0x200
#define B43legacy_MMIO_DMA64_BASE1 0x240
#define B43legacy_MMIO_DMA64_BASE2 0x280
#define B43legacy_MMIO_DMA64_BASE3 0x2C0
#define B43legacy_MMIO_DMA64_BASE4 0x300
#define B43legacy_MMIO_DMA64_BASE5 0x340
/* PIO */
#define B43legacy_MMIO_PIO1_BASE 0x300
#define B43legacy_MMIO_PIO2_BASE 0x310
#define B43legacy_MMIO_PIO3_BASE 0x320
#define B43legacy_MMIO_PIO4_BASE 0x330
#define B43legacy_MMIO_PHY_VER 0x3E0
#define B43legacy_MMIO_PHY_RADIO 0x3E2
#define B43legacy_MMIO_PHY0 0x3E6
#define B43legacy_MMIO_ANTENNA 0x3E8
#define B43legacy_MMIO_CHANNEL 0x3F0
#define B43legacy_MMIO_CHANNEL_EXT 0x3F4
#define B43legacy_MMIO_RADIO_CONTROL 0x3F6
#define B43legacy_MMIO_RADIO_DATA_HIGH 0x3F8
#define B43legacy_MMIO_RADIO_DATA_LOW 0x3FA
#define B43legacy_MMIO_PHY_CONTROL 0x3FC
#define B43legacy_MMIO_PHY_DATA 0x3FE
#define B43legacy_MMIO_MACFILTER_CONTROL 0x420
#define B43legacy_MMIO_MACFILTER_DATA 0x422
#define B43legacy_MMIO_RCMTA_COUNT 0x43C /* Receive Match Transmitter Addr */
#define B43legacy_MMIO_RADIO_HWENABLED_LO 0x49A
#define B43legacy_MMIO_GPIO_CONTROL 0x49C
#define B43legacy_MMIO_GPIO_MASK 0x49E
#define B43legacy_MMIO_TSF_0 0x632 /* core rev < 3 only */
#define B43legacy_MMIO_TSF_1 0x634 /* core rev < 3 only */
#define B43legacy_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define B43legacy_MMIO_TSF_3 0x638 /* core rev < 3 only */
#define B43legacy_MMIO_RNG 0x65A
#define B43legacy_MMIO_POWERUP_DELAY 0x6A8
/* SPROM boardflags_lo values */
#define B43legacy_BFL_PACTRL 0x0002
#define B43legacy_BFL_RSSI 0x0008
#define B43legacy_BFL_EXTLNA 0x1000
/* GPIO register offset, in both ChipCommon and PCI core. */
#define B43legacy_GPIO_CONTROL 0x6c
/* SHM Routing */
#define B43legacy_SHM_SHARED 0x0001
#define B43legacy_SHM_WIRELESS 0x0002
#define B43legacy_SHM_HW 0x0004
#define B43legacy_SHM_UCODE 0x0300
/* SHM Routing modifiers */
#define B43legacy_SHM_AUTOINC_R 0x0200 /* Read Auto-increment */
#define B43legacy_SHM_AUTOINC_W 0x0100 /* Write Auto-increment */
#define B43legacy_SHM_AUTOINC_RW (B43legacy_SHM_AUTOINC_R | \
B43legacy_SHM_AUTOINC_W)
/* Misc SHM_SHARED offsets */
#define B43legacy_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */
#define B43legacy_SHM_SH_HOSTFLO 0x005E /* Hostflags ucode opts (low) */
#define B43legacy_SHM_SH_HOSTFHI 0x0060 /* Hostflags ucode opts (high) */
/* SHM_SHARED crypto engine */
#define B43legacy_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block */
/* SHM_SHARED beacon variables */
#define B43legacy_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word */
/* SHM_SHARED ACK/CTS control */
#define B43legacy_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word */
/* SHM_SHARED probe response variables */
#define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */
#define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */
/* SHM_SHARED rate tables */
/* SHM_SHARED microcode soft registers */
#define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */
#define B43legacy_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */
#define B43legacy_SHM_SH_UCODEDATE 0x0004 /* Microcode date */
#define B43legacy_SHM_SH_UCODETIME 0x0006 /* Microcode time */
#define B43legacy_UCODEFLAGS_OFFSET 0x005E
/* Hardware Radio Enable masks */
#define B43legacy_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16)
#define B43legacy_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4)
/* HostFlags. See b43legacy_hf_read/write() */
#define B43legacy_HF_SYMW 0x00000002 /* G-PHY SYM workaround */
#define B43legacy_HF_GDCW 0x00000020 /* G-PHY DV cancel filter */
#define B43legacy_HF_OFDMPABOOST 0x00000040 /* Enable PA boost OFDM */
#define B43legacy_HF_EDCF 0x00000100 /* on if WME/MAC suspended */
/* MacFilter offsets. */
#define B43legacy_MACFILTER_SELF 0x0000
#define B43legacy_MACFILTER_BSSID 0x0003
#define B43legacy_MACFILTER_MAC 0x0010
/* PHYVersioning */
#define B43legacy_PHYTYPE_B 0x01
#define B43legacy_PHYTYPE_G 0x02
/* PHYRegisters */
#define B43legacy_PHY_G_LO_CONTROL 0x0810
#define B43legacy_PHY_ILT_G_CTRL 0x0472
#define B43legacy_PHY_ILT_G_DATA1 0x0473
#define B43legacy_PHY_ILT_G_DATA2 0x0474
#define B43legacy_PHY_G_PCTL 0x0029
#define B43legacy_PHY_RADIO_BITFIELD 0x0401
#define B43legacy_PHY_G_CRS 0x0429
#define B43legacy_PHY_NRSSILT_CTRL 0x0803
#define B43legacy_PHY_NRSSILT_DATA 0x0804
/* RadioRegisters */
#define B43legacy_RADIOCTL_ID 0x01
/* MAC Control bitfield */
#define B43legacy_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */
#define B43legacy_MACCTL_INFRA 0x00020000 /* Infrastructure mode */
#define B43legacy_MACCTL_AP 0x00040000 /* AccessPoint mode */
#define B43legacy_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep bad PLCP frames */
#define B43legacy_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */
#define B43legacy_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */
#define B43legacy_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */
#define B43legacy_MACCTL_GMODE 0x80000000 /* G Mode */
/* StatusBitField */
#define B43legacy_SBF_MAC_ENABLED 0x00000001
#define B43legacy_SBF_CORE_READY 0x00000004
#define B43legacy_SBF_400 0x00000400 /*FIXME: fix name*/
#define B43legacy_SBF_XFER_REG_BYTESWAP 0x00010000
#define B43legacy_SBF_MODE_NOTADHOC 0x00020000
#define B43legacy_SBF_MODE_AP 0x00040000
#define B43legacy_SBF_RADIOREG_LOCK 0x00080000
#define B43legacy_SBF_MODE_MONITOR 0x00400000
#define B43legacy_SBF_MODE_PROMISC 0x01000000
#define B43legacy_SBF_PS1 0x02000000
#define B43legacy_SBF_PS2 0x04000000
#define B43legacy_SBF_NO_SSID_BCAST 0x08000000
#define B43legacy_SBF_TIME_UPDATE 0x10000000
/* 802.11 core specific TM State Low flags */
#define B43legacy_TMSLOW_GMODE 0x20000000 /* G Mode Enable */
#define B43legacy_TMSLOW_PLLREFSEL 0x00200000 /* PLL Freq Ref Select */
#define B43legacy_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Ctrl Enbl */
#define B43legacy_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */
#define B43legacy_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */
/* 802.11 core specific TM State High flags */
#define B43legacy_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available */
#define B43legacy_TMSHIGH_GPHY 0x00010000 /* G-PHY avail (rev >= 5) */
#define B43legacy_UCODEFLAG_AUTODIV 0x0001
/* Generic-Interrupt reasons. */
#define B43legacy_IRQ_MAC_SUSPENDED 0x00000001
#define B43legacy_IRQ_BEACON 0x00000002
#define B43legacy_IRQ_TBTT_INDI 0x00000004 /* Target Beacon Transmit Time */
#define B43legacy_IRQ_BEACON_TX_OK 0x00000008
#define B43legacy_IRQ_BEACON_CANCEL 0x00000010
#define B43legacy_IRQ_ATIM_END 0x00000020
#define B43legacy_IRQ_PMQ 0x00000040
#define B43legacy_IRQ_PIO_WORKAROUND 0x00000100
#define B43legacy_IRQ_MAC_TXERR 0x00000200
#define B43legacy_IRQ_PHY_TXERR 0x00000800
#define B43legacy_IRQ_PMEVENT 0x00001000
#define B43legacy_IRQ_TIMER0 0x00002000
#define B43legacy_IRQ_TIMER1 0x00004000
#define B43legacy_IRQ_DMA 0x00008000
#define B43legacy_IRQ_TXFIFO_FLUSH_OK 0x00010000
#define B43legacy_IRQ_CCA_MEASURE_OK 0x00020000
#define B43legacy_IRQ_NOISESAMPLE_OK 0x00040000
#define B43legacy_IRQ_UCODE_DEBUG 0x08000000
#define B43legacy_IRQ_RFKILL 0x10000000
#define B43legacy_IRQ_TX_OK 0x20000000
#define B43legacy_IRQ_PHY_G_CHANGED 0x40000000
#define B43legacy_IRQ_TIMEOUT 0x80000000
#define B43legacy_IRQ_ALL 0xFFFFFFFF
#define B43legacy_IRQ_MASKTEMPLATE (B43legacy_IRQ_MAC_SUSPENDED | \
B43legacy_IRQ_BEACON | \
B43legacy_IRQ_TBTT_INDI | \
B43legacy_IRQ_ATIM_END | \
B43legacy_IRQ_PMQ | \
B43legacy_IRQ_MAC_TXERR | \
B43legacy_IRQ_PHY_TXERR | \
B43legacy_IRQ_DMA | \
B43legacy_IRQ_TXFIFO_FLUSH_OK | \
B43legacy_IRQ_NOISESAMPLE_OK | \
B43legacy_IRQ_UCODE_DEBUG | \
B43legacy_IRQ_RFKILL | \
B43legacy_IRQ_TX_OK)
/* Device specific rate values.
* The actual values defined here are (rate_in_mbps * 2).
* Some code depends on this. Don't change it. */
#define B43legacy_CCK_RATE_1MB 2
#define B43legacy_CCK_RATE_2MB 4
#define B43legacy_CCK_RATE_5MB 11
#define B43legacy_CCK_RATE_11MB 22
#define B43legacy_OFDM_RATE_6MB 12
#define B43legacy_OFDM_RATE_9MB 18
#define B43legacy_OFDM_RATE_12MB 24
#define B43legacy_OFDM_RATE_18MB 36
#define B43legacy_OFDM_RATE_24MB 48
#define B43legacy_OFDM_RATE_36MB 72
#define B43legacy_OFDM_RATE_48MB 96
#define B43legacy_OFDM_RATE_54MB 108
/* Convert a b43legacy rate value to a rate in 100kbps */
#define B43legacy_RATE_TO_100KBPS(rate) (((rate) * 10) / 2)
#define B43legacy_DEFAULT_SHORT_RETRY_LIMIT 7
#define B43legacy_DEFAULT_LONG_RETRY_LIMIT 4
/* Max size of a security key */
#define B43legacy_SEC_KEYSIZE 16
/* Security algorithms. */
enum {
B43legacy_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */
B43legacy_SEC_ALGO_WEP40,
B43legacy_SEC_ALGO_TKIP,
B43legacy_SEC_ALGO_AES,
B43legacy_SEC_ALGO_WEP104,
B43legacy_SEC_ALGO_AES_LEGACY,
};
/* Core Information Registers */
#define B43legacy_CIR_BASE 0xf00
#define B43legacy_CIR_SBTPSFLAG (B43legacy_CIR_BASE + 0x18)
#define B43legacy_CIR_SBIMSTATE (B43legacy_CIR_BASE + 0x90)
#define B43legacy_CIR_SBINTVEC (B43legacy_CIR_BASE + 0x94)
#define B43legacy_CIR_SBTMSTATELOW (B43legacy_CIR_BASE + 0x98)
#define B43legacy_CIR_SBTMSTATEHIGH (B43legacy_CIR_BASE + 0x9c)
#define B43legacy_CIR_SBIMCONFIGLOW (B43legacy_CIR_BASE + 0xa8)
#define B43legacy_CIR_SB_ID_HI (B43legacy_CIR_BASE + 0xfc)
/* sbtmstatehigh state flags */
#define B43legacy_SBTMSTATEHIGH_SERROR 0x00000001
#define B43legacy_SBTMSTATEHIGH_BUSY 0x00000004
#define B43legacy_SBTMSTATEHIGH_TIMEOUT 0x00000020
#define B43legacy_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000
#define B43legacy_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000
#define B43legacy_SBTMSTATEHIGH_DMA64BIT 0x10000000
#define B43legacy_SBTMSTATEHIGH_GATEDCLK 0x20000000
#define B43legacy_SBTMSTATEHIGH_BISTFAILED 0x40000000
#define B43legacy_SBTMSTATEHIGH_BISTCOMPLETE 0x80000000
/* sbimstate flags */
#define B43legacy_SBIMSTATE_IB_ERROR 0x20000
#define B43legacy_SBIMSTATE_TIMEOUT 0x40000
#define PFX KBUILD_MODNAME ": "
#ifdef assert
# undef assert
#endif
#ifdef CONFIG_B43LEGACY_DEBUG
# define B43legacy_WARN_ON(expr) \
do { \
if (unlikely((expr))) { \
printk(KERN_INFO PFX "Test (%s) failed at:" \
" %s:%d:%s()\n", \
#expr, __FILE__, \
__LINE__, __FUNCTION__); \
} \
} while (0)
# define B43legacy_BUG_ON(expr) \
do { \
if (unlikely((expr))) { \
printk(KERN_INFO PFX "Test (%s) failed\n", \
#expr); \
BUG_ON(expr); \
} \
} while (0)
# define B43legacy_DEBUG 1
#else
# define B43legacy_WARN_ON(x) do { /* nothing */ } while (0)
# define B43legacy_BUG_ON(x) do { /* nothing */ } while (0)
# define B43legacy_DEBUG 0
#endif
struct net_device;
struct pci_dev;
struct b43legacy_dmaring;
struct b43legacy_pioqueue;
/* The firmware file header */
#define B43legacy_FW_TYPE_UCODE 'u'
#define B43legacy_FW_TYPE_PCM 'p'
#define B43legacy_FW_TYPE_IV 'i'
struct b43legacy_fw_header {
/* File type */
u8 type;
/* File format version */
u8 ver;
u8 __padding[2];
/* Size of the data. For ucode and PCM this is in bytes.
* For IV this is number-of-ivs. */
__be32 size;
} __attribute__((__packed__));
/* Initial Value file format */
#define B43legacy_IV_OFFSET_MASK 0x7FFF
#define B43legacy_IV_32BIT 0x8000
struct b43legacy_iv {
__be16 offset_size;
union {
__be16 d16;
__be32 d32;
} data __attribute__((__packed__));
} __attribute__((__packed__));
#define B43legacy_PHYMODE(phytype) (1 << (phytype))
#define B43legacy_PHYMODE_B B43legacy_PHYMODE \
((B43legacy_PHYTYPE_B))
#define B43legacy_PHYMODE_G B43legacy_PHYMODE \
((B43legacy_PHYTYPE_G))
/* Value pair to measure the LocalOscillator. */
struct b43legacy_lopair {
s8 low;
s8 high;
u8 used:1;
};
#define B43legacy_LO_COUNT (14*4)
struct b43legacy_phy {
/* Possible PHYMODEs on this PHY */
u8 possible_phymodes;
/* GMODE bit enabled in MACCTL? */
bool gmode;
/* Possible ieee80211 subsystem hwmodes for this PHY.
* Which mode is selected, depends on thr GMODE enabled bit */
#define B43legacy_MAX_PHYHWMODES 2
struct ieee80211_hw_mode hwmodes[B43legacy_MAX_PHYHWMODES];
/* Analog Type */
u8 analog;
/* B43legacy_PHYTYPE_ */
u8 type;
/* PHY revision number. */
u8 rev;
u16 antenna_diversity;
u16 savedpctlreg;
/* Radio versioning */
u16 radio_manuf; /* Radio manufacturer */
u16 radio_ver; /* Radio version */
u8 calibrated:1;
u8 radio_rev; /* Radio revision */
bool radio_on; /* Radio switched on/off */
bool locked; /* Only used in b43legacy_phy_{un}lock() */
bool dyn_tssi_tbl; /* tssi2dbm is kmalloc()ed. */
/* ACI (adjacent channel interference) flags. */
bool aci_enable;
bool aci_wlan_automatic;
bool aci_hw_rssi;
u16 minlowsig[2];
u16 minlowsigpos[2];
/* LO Measurement Data.
* Use b43legacy_get_lopair() to get a value.
*/
struct b43legacy_lopair *_lo_pairs;
/* TSSI to dBm table in use */
const s8 *tssi2dbm;
/* idle TSSI value */
s8 idle_tssi;
/* Target idle TSSI */
int tgt_idle_tssi;
/* Current idle TSSI */
int cur_idle_tssi;
/* LocalOscillator control values. */
struct b43legacy_txpower_lo_control *lo_control;
/* Values from b43legacy_calc_loopback_gain() */
s16 max_lb_gain; /* Maximum Loopback gain in hdB */
s16 trsw_rx_gain; /* TRSW RX gain in hdB */
s16 lna_lod_gain; /* LNA lod */
s16 lna_gain; /* LNA */
s16 pga_gain; /* PGA */
/* PHY lock for core.rev < 3
* This lock is only used by b43legacy_phy_{un}lock()
*/
spinlock_t lock;
/* Desired TX power level (in dBm). This is set by the user and
* adjusted in b43legacy_phy_xmitpower(). */
u8 power_level;
/* Values from b43legacy_calc_loopback_gain() */
u16 loopback_gain[2];
/* TX Power control values. */
/* B/G PHY */
struct {
/* Current Radio Attenuation for TXpower recalculation. */
u16 rfatt;
/* Current Baseband Attenuation for TXpower recalculation. */
u16 bbatt;
/* Current TXpower control value for TXpower recalculation. */
u16 txctl1;
u16 txctl2;
};
/* A PHY */
struct {
u16 txpwr_offset;
};
#ifdef CONFIG_B43LEGACY_DEBUG
bool manual_txpower_control; /* Manual TX-power control enabled? */
#endif
/* Current Interference Mitigation mode */
int interfmode;
/* Stack of saved values from the Interference Mitigation code.
* Each value in the stack is layed out as follows:
* bit 0-11: offset
* bit 12-15: register ID
* bit 16-32: value
* register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT
*/
#define B43legacy_INTERFSTACK_SIZE 26
u32 interfstack[B43legacy_INTERFSTACK_SIZE];
/* Saved values from the NRSSI Slope calculation */
s16 nrssi[2];
s32 nrssislope;
/* In memory nrssi lookup table. */
s8 nrssi_lt[64];
/* current channel */
u8 channel;
u16 lofcal;
u16 initval;
};
/* Data structures for DMA transmission, per 80211 core. */
struct b43legacy_dma {
struct b43legacy_dmaring *tx_ring0;
struct b43legacy_dmaring *tx_ring1;
struct b43legacy_dmaring *tx_ring2;
struct b43legacy_dmaring *tx_ring3;
struct b43legacy_dmaring *tx_ring4;
struct b43legacy_dmaring *tx_ring5;
struct b43legacy_dmaring *rx_ring0;
struct b43legacy_dmaring *rx_ring3; /* only on core.rev < 5 */
};
/* Data structures for PIO transmission, per 80211 core. */
struct b43legacy_pio {
struct b43legacy_pioqueue *queue0;
struct b43legacy_pioqueue *queue1;
struct b43legacy_pioqueue *queue2;
struct b43legacy_pioqueue *queue3;
};
/* Context information for a noise calculation (Link Quality). */
struct b43legacy_noise_calculation {
u8 channel_at_start;
bool calculation_running;
u8 nr_samples;
s8 samples[8][4];
};
struct b43legacy_stats {
u8 link_noise;
/* Store the last TX/RX times here for updating the leds. */
unsigned long last_tx;
unsigned long last_rx;
};
struct b43legacy_key {
void *keyconf;
bool enabled;
u8 algorithm;
};
struct b43legacy_wldev;
/* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */
struct b43legacy_wl {
/* Pointer to the active wireless device on this chip */
struct b43legacy_wldev *current_dev;
/* Pointer to the ieee80211 hardware data structure */
struct ieee80211_hw *hw;
spinlock_t irq_lock; /* locks IRQ */
struct mutex mutex; /* locks wireless core state */
spinlock_t leds_lock; /* lock for leds */
/* We can only have one operating interface (802.11 core)
* at a time. General information about this interface follows.
*/
/* Opaque ID of the operating interface (!= monitor
* interface) from the ieee80211 subsystem.
* Do not modify.
*/
int if_id;
/* MAC address (can be NULL). */
const u8 *mac_addr;
/* Current BSSID (can be NULL). */
const u8 *bssid;
/* Interface type. (IEEE80211_IF_TYPE_XXX) */
int if_type;
/* Counter of active monitor interfaces. */
int monitor;
/* Is the card operating in AP, STA or IBSS mode? */
bool operating;
/* Promisc mode active?
* Note that (monitor != 0) implies promisc.
*/
bool promisc;
/* Stats about the wireless interface */
struct ieee80211_low_level_stats ieee_stats;
struct hwrng rng;
u8 rng_initialized;
char rng_name[30 + 1];
/* List of all wireless devices on this chip */
struct list_head devlist;
u8 nr_devs;
};
/* Pointers to the firmware data and meta information about it. */
struct b43legacy_firmware {
/* Microcode */
const struct firmware *ucode;
/* PCM code */
const struct firmware *pcm;
/* Initial MMIO values for the firmware */
const struct firmware *initvals;
/* Initial MMIO values for the firmware, band-specific */
const struct firmware *initvals_band;
/* Firmware revision */
u16 rev;
/* Firmware patchlevel */
u16 patch;
};
/* Device (802.11 core) initialization status. */
enum {
B43legacy_STAT_UNINIT = 0, /* Uninitialized. */
B43legacy_STAT_INITIALIZED = 1, /* Initialized, not yet started. */
B43legacy_STAT_STARTED = 2, /* Up and running. */
};
#define b43legacy_status(wldev) atomic_read(&(wldev)->__init_status)
#define b43legacy_set_status(wldev, stat) do { \
atomic_set(&(wldev)->__init_status, (stat)); \
smp_wmb(); \
} while (0)
/* *** --- HOW LOCKING WORKS IN B43legacy --- ***
*
* You should always acquire both, wl->mutex and wl->irq_lock unless:
* - You don't need to acquire wl->irq_lock, if the interface is stopped.
* - You don't need to acquire wl->mutex in the IRQ handler, IRQ tasklet
* and packet TX path (and _ONLY_ there.)
*/
/* Data structure for one wireless device (802.11 core) */
struct b43legacy_wldev {
struct ssb_device *dev;
struct b43legacy_wl *wl;
/* The device initialization status.
* Use b43legacy_status() to query. */
atomic_t __init_status;
/* Saved init status for handling suspend. */
int suspend_init_status;
bool __using_pio; /* Using pio rather than dma. */
bool bad_frames_preempt;/* Use "Bad Frames Preemption". */
bool reg124_set_0x4; /* Variable to keep track of IRQ. */
bool short_preamble; /* TRUE if using short preamble. */
bool short_slot; /* TRUE if using short slot timing. */
bool radio_hw_enable; /* State of radio hardware enable bit. */
/* PHY/Radio device. */
struct b43legacy_phy phy;
union {
/* DMA engines. */
struct b43legacy_dma dma;
/* PIO engines. */
struct b43legacy_pio pio;
};
/* Various statistics about the physical device. */
struct b43legacy_stats stats;
#define B43legacy_NR_LEDS 4
struct b43legacy_led leds[B43legacy_NR_LEDS];
/* Reason code of the last interrupt. */
u32 irq_reason;
u32 dma_reason[6];
/* saved irq enable/disable state bitfield. */
u32 irq_savedstate;
/* Link Quality calculation context. */
struct b43legacy_noise_calculation noisecalc;
/* if > 0 MAC is suspended. if == 0 MAC is enabled. */
int mac_suspended;
/* Interrupt Service Routine tasklet (bottom-half) */
struct tasklet_struct isr_tasklet;
/* Periodic tasks */
struct delayed_work periodic_work;
unsigned int periodic_state;
struct work_struct restart_work;
/* encryption/decryption */
u16 ktp; /* Key table pointer */
u8 max_nr_keys;
struct b43legacy_key key[58];
/* Cached beacon template while uploading the template. */
struct sk_buff *cached_beacon;
/* Firmware data */
struct b43legacy_firmware fw;
/* Devicelist in struct b43legacy_wl (all 802.11 cores) */
struct list_head list;
/* Debugging stuff follows. */
#ifdef CONFIG_B43LEGACY_DEBUG
struct b43legacy_dfsentry *dfsentry;
#endif
};
static inline
struct b43legacy_wl *hw_to_b43legacy_wl(struct ieee80211_hw *hw)
{
return hw->priv;
}
/* Helper function, which returns a boolean.
* TRUE, if PIO is used; FALSE, if DMA is used.
*/
#if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO)
static inline
int b43legacy_using_pio(struct b43legacy_wldev *dev)
{
return dev->__using_pio;
}
#elif defined(CONFIG_B43LEGACY_DMA)
static inline
int b43legacy_using_pio(struct b43legacy_wldev *dev)
{
return 0;
}
#elif defined(CONFIG_B43LEGACY_PIO)
static inline
int b43legacy_using_pio(struct b43legacy_wldev *dev)
{
return 1;
}
#else
# error "Using neither DMA nor PIO? Confused..."
#endif
static inline
struct b43legacy_wldev *dev_to_b43legacy_wldev(struct device *dev)
{
struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
return ssb_get_drvdata(ssb_dev);
}
/* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */
static inline
int b43legacy_is_mode(struct b43legacy_wl *wl, int type)
{
if (type == IEEE80211_IF_TYPE_MNTR)
return !!(wl->monitor);
return (wl->operating &&
wl->if_type == type);
}
static inline
bool is_bcm_board_vendor(struct b43legacy_wldev *dev)
{
return (dev->dev->bus->boardinfo.vendor == PCI_VENDOR_ID_BROADCOM);
}
static inline
u16 b43legacy_read16(struct b43legacy_wldev *dev, u16 offset)
{
return ssb_read16(dev->dev, offset);
}
static inline
void b43legacy_write16(struct b43legacy_wldev *dev, u16 offset, u16 value)
{
ssb_write16(dev->dev, offset, value);
}
static inline
u32 b43legacy_read32(struct b43legacy_wldev *dev, u16 offset)
{
return ssb_read32(dev->dev, offset);
}
static inline
void b43legacy_write32(struct b43legacy_wldev *dev, u16 offset, u32 value)
{
ssb_write32(dev->dev, offset, value);
}
static inline
struct b43legacy_lopair *b43legacy_get_lopair(struct b43legacy_phy *phy,
u16 radio_attenuation,
u16 baseband_attenuation)
{
return phy->_lo_pairs + (radio_attenuation
+ 14 * (baseband_attenuation / 2));
}
/* Message printing */
void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#if B43legacy_DEBUG
void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#else /* DEBUG */
# define b43legacydbg(wl, fmt...) do { /* nothing */ } while (0)
#endif /* DEBUG */
/** Limit a value between two limits */
#ifdef limit_value
# undef limit_value
#endif
#define limit_value(value, min, max) \
({ \
typeof(value) __value = (value); \
typeof(value) __min = (min); \
typeof(value) __max = (max); \
if (__value < __min) \
__value = __min; \
else if (__value > __max) \
__value = __max; \
__value; \
})
/* Macros for printing a value in Q5.2 format */
#define Q52_FMT "%u.%u"
#define Q52_ARG(q52) ((q52) / 4), (((q52) & 3) * 100 / 4)
#endif /* B43legacy_H_ */
/*
Broadcom B43legacy wireless driver
debugfs driver debugging code
Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
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/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/mutex.h>
#include "b43legacy.h"
#include "main.h"
#include "debugfs.h"
#include "dma.h"
#include "pio.h"
#include "xmit.h"
/* The root directory. */
static struct dentry *rootdir;
struct b43legacy_debugfs_fops {
ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize);
int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count);
struct file_operations fops;
/* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */
size_t file_struct_offset;
/* Take wl->irq_lock before calling read/write? */
bool take_irqlock;
};
static inline
struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev,
const struct b43legacy_debugfs_fops *dfops)
{
void *p;
p = dev->dfsentry;
p += dfops->file_struct_offset;
return p;
}
#define fappend(fmt, x...) \
do { \
if (bufsize - count) \
count += snprintf(buf + count, \
bufsize - count, \
fmt , ##x); \
else \
printk(KERN_ERR "b43legacy: fappend overflow\n"); \
} while (0)
/* wl->irq_lock is locked */
static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
{
ssize_t count = 0;
u64 tsf;
b43legacy_tsf_read(dev, &tsf);
fappend("0x%08x%08x\n",
(unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
(unsigned int)(tsf & 0xFFFFFFFFULL));
return count;
}
/* wl->irq_lock is locked */
static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
{
u64 tsf;
if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1)
return -EINVAL;
b43legacy_tsf_write(dev, tsf);
return 0;
}
/* wl->irq_lock is locked */
static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
{
ssize_t count = 0;
int i;
for (i = 0; i < 64; i++) {
fappend("r%d = 0x%04x\n", i,
b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i));
}
return count;
}
/* wl->irq_lock is locked */
static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
{
ssize_t count = 0;
int i;
u16 tmp;
__le16 *le16buf = (__le16 *)buf;
for (i = 0; i < 0x1000; i++) {
if (bufsize <= 0)
break;
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i);
le16buf[i] = cpu_to_le16(tmp);
count += sizeof(tmp);
bufsize -= sizeof(tmp);
}
return count;
}
static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
{
struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog;
ssize_t count = 0;
unsigned long flags;
int i, idx;
struct b43legacy_txstatus *stat;
spin_lock_irqsave(&log->lock, flags);
if (log->end < 0) {
fappend("Nothing transmitted, yet\n");
goto out_unlock;
}
fappend("b43legacy TX status reports:\n\n"
"index | cookie | seq | phy_stat | frame_count | "
"rts_count | supp_reason | pm_indicated | "
"intermediate | for_ampdu | acked\n" "---\n");
i = log->end + 1;
idx = 0;
while (1) {
if (i == B43legacy_NR_LOGGED_TXSTATUS)
i = 0;
stat = &(log->log[i]);
if (stat->cookie) {
fappend("%03d | "
"0x%04X | 0x%04X | 0x%02X | "
"0x%X | 0x%X | "
"%u | %u | "
"%u | %u | %u\n",
idx,
stat->cookie, stat->seq, stat->phy_stat,
stat->frame_count, stat->rts_count,
stat->supp_reason, stat->pm_indicated,
stat->intermediate, stat->for_ampdu,
stat->acked);
idx++;
}
if (i == log->end)
break;
i++;
}
out_unlock:
spin_unlock_irqrestore(&log->lock, flags);
return count;
}
/* wl->irq_lock is locked */
static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
{
int err = 0;
if (count > 0 && buf[0] == '1') {
b43legacy_controller_restart(dev, "manually restarted");
} else
err = -EINVAL;
return err;
}
#undef fappend
static int b43legacy_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct b43legacy_wldev *dev;
struct b43legacy_debugfs_fops *dfops;
struct b43legacy_dfs_file *dfile;
ssize_t ret = 0;
char *buf;
const size_t bufsize = 1024 * 128;
const size_t buforder = get_order(bufsize);
int err = 0;
if (!count)
return 0;
dev = file->private_data;
if (!dev)
return -ENODEV;
mutex_lock(&dev->wl->mutex);
if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
err = -ENODEV;
goto out_unlock;
}
dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops);
if (!dfops->read) {
err = -ENOSYS;
goto out_unlock;
}
dfile = fops_to_dfs_file(dev, dfops);
if (!dfile->buffer) {
buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
if (!buf) {
err = -ENOMEM;
goto out_unlock;
}
memset(buf, 0, bufsize);
if (dfops->take_irqlock) {
spin_lock_irq(&dev->wl->irq_lock);
ret = dfops->read(dev, buf, bufsize);
spin_unlock_irq(&dev->wl->irq_lock);
} else
ret = dfops->read(dev, buf, bufsize);
if (ret <= 0) {
free_pages((unsigned long)buf, buforder);
err = ret;
goto out_unlock;
}
dfile->data_len = ret;
dfile->buffer = buf;
}
ret = simple_read_from_buffer(userbuf, count, ppos,
dfile->buffer,
dfile->data_len);
if (*ppos >= dfile->data_len) {
free_pages((unsigned long)dfile->buffer, buforder);
dfile->buffer = NULL;
dfile->data_len = 0;
}
out_unlock:
mutex_unlock(&dev->wl->mutex);
return err ? err : ret;
}
static ssize_t b43legacy_debugfs_write(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct b43legacy_wldev *dev;
struct b43legacy_debugfs_fops *dfops;
char *buf;
int err = 0;
if (!count)
return 0;
if (count > PAGE_SIZE)
return -E2BIG;
dev = file->private_data;
if (!dev)
return -ENODEV;
mutex_lock(&dev->wl->mutex);
if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
err = -ENODEV;
goto out_unlock;
}
dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops);
if (!dfops->write) {
err = -ENOSYS;
goto out_unlock;
}
buf = (char *)get_zeroed_page(GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto out_unlock;
}
if (copy_from_user(buf, userbuf, count)) {
err = -EFAULT;
goto out_freepage;
}
if (dfops->take_irqlock) {
spin_lock_irq(&dev->wl->irq_lock);
err = dfops->write(dev, buf, count);
spin_unlock_irq(&dev->wl->irq_lock);
} else
err = dfops->write(dev, buf, count);
if (err)
goto out_freepage;
out_freepage:
free_page((unsigned long)buf);
out_unlock:
mutex_unlock(&dev->wl->mutex);
return err ? err : count;
}
#define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \
static struct b43legacy_debugfs_fops fops_##name = { \
.read = _read, \
.write = _write, \
.fops = { \
.open = b43legacy_debugfs_open, \
.read = b43legacy_debugfs_read, \
.write = b43legacy_debugfs_write, \
}, \
.file_struct_offset = offsetof(struct b43legacy_dfsentry, \
file_##name), \
.take_irqlock = _take_irqlock, \
}
B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);
B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);
B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);
B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature)
{
return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
}
static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev)
{
struct b43legacy_dfsentry *e = dev->dfsentry;
int i;
for (i = 0; i < __B43legacy_NR_DYNDBG; i++)
debugfs_remove(e->dyn_debug_dentries[i]);
}
static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)
{
struct b43legacy_dfsentry *e = dev->dfsentry;
struct dentry *d;
#define add_dyn_dbg(name, id, initstate) do { \
e->dyn_debug[id] = (initstate); \
d = debugfs_create_bool(name, 0600, e->subdir, \
&(e->dyn_debug[id])); \
if (!IS_ERR(d)) \
e->dyn_debug_dentries[id] = d; \
} while (0)
add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, 0);
add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, 0);
add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, 0);
add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, 0);
add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, 0);
#undef add_dyn_dbg
}
void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)
{
struct b43legacy_dfsentry *e;
struct b43legacy_txstatus_log *log;
char devdir[16];
B43legacy_WARN_ON(!dev);
e = kzalloc(sizeof(*e), GFP_KERNEL);
if (!e) {
b43legacyerr(dev->wl, "debugfs: add device OOM\n");
return;
}
e->dev = dev;
log = &e->txstatlog;
log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS,
sizeof(struct b43legacy_txstatus), GFP_KERNEL);
if (!log->log) {
b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n");
kfree(e);
return;
}
log->end = -1;
spin_lock_init(&log->lock);
dev->dfsentry = e;
snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
e->subdir = debugfs_create_dir(devdir, rootdir);
if (!e->subdir || IS_ERR(e->subdir)) {
if (e->subdir == ERR_PTR(-ENODEV)) {
b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
"enabled in kernel config\n");
} else {
b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n",
devdir);
}
dev->dfsentry = NULL;
kfree(log->log);
kfree(e);
return;
}
#define ADD_FILE(name, mode) \
do { \
struct dentry *d; \
d = debugfs_create_file(__stringify(name), \
mode, e->subdir, dev, \
&fops_##name.fops); \
e->file_##name.dentry = NULL; \
if (!IS_ERR(d)) \
e->file_##name.dentry = d; \
} while (0)
ADD_FILE(tsf, 0600);
ADD_FILE(ucode_regs, 0400);
ADD_FILE(shm, 0400);
ADD_FILE(txstat, 0400);
ADD_FILE(restart, 0200);
#undef ADD_FILE
b43legacy_add_dynamic_debug(dev);
}
void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev)
{
struct b43legacy_dfsentry *e;
if (!dev)
return;
e = dev->dfsentry;
if (!e)
return;
b43legacy_remove_dynamic_debug(dev);
debugfs_remove(e->file_tsf.dentry);
debugfs_remove(e->file_ucode_regs.dentry);
debugfs_remove(e->file_shm.dentry);
debugfs_remove(e->file_txstat.dentry);
debugfs_remove(e->file_restart.dentry);
debugfs_remove(e->subdir);
kfree(e->txstatlog.log);
kfree(e);
}
void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
struct b43legacy_dfsentry *e = dev->dfsentry;
struct b43legacy_txstatus_log *log;
struct b43legacy_txstatus *cur;
int i;
if (!e)
return;
log = &e->txstatlog;
B43legacy_WARN_ON(!irqs_disabled());
spin_lock(&log->lock);
i = log->end + 1;
if (i == B43legacy_NR_LOGGED_TXSTATUS)
i = 0;
log->end = i;
cur = &(log->log[i]);
memcpy(cur, status, sizeof(*cur));
spin_unlock(&log->lock);
}
void b43legacy_debugfs_init(void)
{
rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
if (IS_ERR(rootdir))
rootdir = NULL;
}
void b43legacy_debugfs_exit(void)
{
debugfs_remove(rootdir);
}
#ifndef B43legacy_DEBUGFS_H_
#define B43legacy_DEBUGFS_H_
struct b43legacy_wldev;
struct b43legacy_txstatus;
enum b43legacy_dyndbg { /* Dynamic debugging features */
B43legacy_DBG_XMITPOWER,
B43legacy_DBG_DMAOVERFLOW,
B43legacy_DBG_DMAVERBOSE,
B43legacy_DBG_PWORK_FAST,
B43legacy_DBG_PWORK_STOP,
__B43legacy_NR_DYNDBG,
};
#ifdef CONFIG_B43LEGACY_DEBUG
struct dentry;
#define B43legacy_NR_LOGGED_TXSTATUS 100
struct b43legacy_txstatus_log {
struct b43legacy_txstatus *log;
int end;
spinlock_t lock; /* lock for debugging */
};
struct b43legacy_dfs_file {
struct dentry *dentry;
char *buffer;
size_t data_len;
};
struct b43legacy_dfsentry {
struct b43legacy_wldev *dev;
struct dentry *subdir;
struct b43legacy_dfs_file file_tsf;
struct b43legacy_dfs_file file_ucode_regs;
struct b43legacy_dfs_file file_shm;
struct b43legacy_dfs_file file_txstat;
struct b43legacy_dfs_file file_txpower_g;
struct b43legacy_dfs_file file_restart;
struct b43legacy_dfs_file file_loctls;
struct b43legacy_txstatus_log txstatlog;
/* Enabled/Disabled list for the dynamic debugging features. */
u32 dyn_debug[__B43legacy_NR_DYNDBG];
/* Dentries for the dynamic debugging entries. */
struct dentry *dyn_debug_dentries[__B43legacy_NR_DYNDBG];
};
int b43legacy_debug(struct b43legacy_wldev *dev,
enum b43legacy_dyndbg feature);
void b43legacy_debugfs_init(void);
void b43legacy_debugfs_exit(void);
void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev);
void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev);
void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status);
#else /* CONFIG_B43LEGACY_DEBUG*/
static inline
int b43legacy_debug(struct b43legacy_wldev *dev,
enum b43legacy_dyndbg feature)
{
return 0;
}
static inline
void b43legacy_debugfs_init(void) { }
static inline
void b43legacy_debugfs_exit(void) { }
static inline
void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) { }
static inline
void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) { }
static inline
void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{ }
#endif /* CONFIG_B43LEGACY_DEBUG*/
#endif /* B43legacy_DEBUGFS_H_ */
/*
Broadcom B43legacy wireless driver
DMA ringbuffer and descriptor allocation/management
Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
Some code in this file is derived from the b44.c driver
Copyright (C) 2002 David S. Miller
Copyright (C) Pekka Pietikainen
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 "b43legacy.h"
#include "dma.h"
#include "main.h"
#include "debugfs.h"
#include "xmit.h"
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <net/dst.h>
/* 32bit DMA ops. */
static
struct b43legacy_dmadesc_generic *op32_idx2desc(
struct b43legacy_dmaring *ring,
int slot,
struct b43legacy_dmadesc_meta **meta)
{
struct b43legacy_dmadesc32 *desc;
*meta = &(ring->meta[slot]);
desc = ring->descbase;
desc = &(desc[slot]);
return (struct b43legacy_dmadesc_generic *)desc;
}
static void op32_fill_descriptor(struct b43legacy_dmaring *ring,
struct b43legacy_dmadesc_generic *desc,
dma_addr_t dmaaddr, u16 bufsize,
int start, int end, int irq)
{
struct b43legacy_dmadesc32 *descbase = ring->descbase;
int slot;
u32 ctl;
u32 addr;
u32 addrext;
slot = (int)(&(desc->dma32) - descbase);
B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK);
addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
addr |= ssb_dma_translation(ring->dev->dev);
ctl = (bufsize - ring->frameoffset)
& B43legacy_DMA32_DCTL_BYTECNT;
if (slot == ring->nr_slots - 1)
ctl |= B43legacy_DMA32_DCTL_DTABLEEND;
if (start)
ctl |= B43legacy_DMA32_DCTL_FRAMESTART;
if (end)
ctl |= B43legacy_DMA32_DCTL_FRAMEEND;
if (irq)
ctl |= B43legacy_DMA32_DCTL_IRQ;
ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT)
& B43legacy_DMA32_DCTL_ADDREXT_MASK;
desc->dma32.control = cpu_to_le32(ctl);
desc->dma32.address = cpu_to_le32(addr);
}
static void op32_poke_tx(struct b43legacy_dmaring *ring, int slot)
{
b43legacy_dma_write(ring, B43legacy_DMA32_TXINDEX,
(u32)(slot * sizeof(struct b43legacy_dmadesc32)));
}
static void op32_tx_suspend(struct b43legacy_dmaring *ring)
{
b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL,
b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL)
| B43legacy_DMA32_TXSUSPEND);
}
static void op32_tx_resume(struct b43legacy_dmaring *ring)
{
b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL,
b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL)
& ~B43legacy_DMA32_TXSUSPEND);
}
static int op32_get_current_rxslot(struct b43legacy_dmaring *ring)
{
u32 val;
val = b43legacy_dma_read(ring, B43legacy_DMA32_RXSTATUS);
val &= B43legacy_DMA32_RXDPTR;
return (val / sizeof(struct b43legacy_dmadesc32));
}
static void op32_set_current_rxslot(struct b43legacy_dmaring *ring,
int slot)
{
b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX,
(u32)(slot * sizeof(struct b43legacy_dmadesc32)));
}
static const struct b43legacy_dma_ops dma32_ops = {
.idx2desc = op32_idx2desc,
.fill_descriptor = op32_fill_descriptor,
.poke_tx = op32_poke_tx,
.tx_suspend = op32_tx_suspend,
.tx_resume = op32_tx_resume,
.get_current_rxslot = op32_get_current_rxslot,
.set_current_rxslot = op32_set_current_rxslot,
};
/* 64bit DMA ops. */
static
struct b43legacy_dmadesc_generic *op64_idx2desc(
struct b43legacy_dmaring *ring,
int slot,
struct b43legacy_dmadesc_meta
**meta)
{
struct b43legacy_dmadesc64 *desc;
*meta = &(ring->meta[slot]);
desc = ring->descbase;
desc = &(desc[slot]);
return (struct b43legacy_dmadesc_generic *)desc;
}
static void op64_fill_descriptor(struct b43legacy_dmaring *ring,
struct b43legacy_dmadesc_generic *desc,
dma_addr_t dmaaddr, u16 bufsize,
int start, int end, int irq)
{
struct b43legacy_dmadesc64 *descbase = ring->descbase;
int slot;
u32 ctl0 = 0;
u32 ctl1 = 0;
u32 addrlo;
u32 addrhi;
u32 addrext;
slot = (int)(&(desc->dma64) - descbase);
B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
addrlo = (u32)(dmaaddr & 0xFFFFFFFF);
addrhi = (((u64)dmaaddr >> 32) & ~SSB_DMA_TRANSLATION_MASK);
addrext = (((u64)dmaaddr >> 32) & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
addrhi |= ssb_dma_translation(ring->dev->dev);
if (slot == ring->nr_slots - 1)
ctl0 |= B43legacy_DMA64_DCTL0_DTABLEEND;
if (start)
ctl0 |= B43legacy_DMA64_DCTL0_FRAMESTART;
if (end)
ctl0 |= B43legacy_DMA64_DCTL0_FRAMEEND;
if (irq)
ctl0 |= B43legacy_DMA64_DCTL0_IRQ;
ctl1 |= (bufsize - ring->frameoffset)
& B43legacy_DMA64_DCTL1_BYTECNT;
ctl1 |= (addrext << B43legacy_DMA64_DCTL1_ADDREXT_SHIFT)
& B43legacy_DMA64_DCTL1_ADDREXT_MASK;
desc->dma64.control0 = cpu_to_le32(ctl0);
desc->dma64.control1 = cpu_to_le32(ctl1);
desc->dma64.address_low = cpu_to_le32(addrlo);
desc->dma64.address_high = cpu_to_le32(addrhi);
}
static void op64_poke_tx(struct b43legacy_dmaring *ring, int slot)
{
b43legacy_dma_write(ring, B43legacy_DMA64_TXINDEX,
(u32)(slot * sizeof(struct b43legacy_dmadesc64)));
}
static void op64_tx_suspend(struct b43legacy_dmaring *ring)
{
b43legacy_dma_write(ring, B43legacy_DMA64_TXCTL,
b43legacy_dma_read(ring, B43legacy_DMA64_TXCTL)
| B43legacy_DMA64_TXSUSPEND);
}
static void op64_tx_resume(struct b43legacy_dmaring *ring)
{
b43legacy_dma_write(ring, B43legacy_DMA64_TXCTL,
b43legacy_dma_read(ring, B43legacy_DMA64_TXCTL)
& ~B43legacy_DMA64_TXSUSPEND);
}
static int op64_get_current_rxslot(struct b43legacy_dmaring *ring)
{
u32 val;
val = b43legacy_dma_read(ring, B43legacy_DMA64_RXSTATUS);
val &= B43legacy_DMA64_RXSTATDPTR;
return (val / sizeof(struct b43legacy_dmadesc64));
}
static void op64_set_current_rxslot(struct b43legacy_dmaring *ring,
int slot)
{
b43legacy_dma_write(ring, B43legacy_DMA64_RXINDEX,
(u32)(slot * sizeof(struct b43legacy_dmadesc64)));
}
static const struct b43legacy_dma_ops dma64_ops = {
.idx2desc = op64_idx2desc,
.fill_descriptor = op64_fill_descriptor,
.poke_tx = op64_poke_tx,
.tx_suspend = op64_tx_suspend,
.tx_resume = op64_tx_resume,
.get_current_rxslot = op64_get_current_rxslot,
.set_current_rxslot = op64_set_current_rxslot,
};
static inline int free_slots(struct b43legacy_dmaring *ring)
{
return (ring->nr_slots - ring->used_slots);
}
static inline int next_slot(struct b43legacy_dmaring *ring, int slot)
{
B43legacy_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1));
if (slot == ring->nr_slots - 1)
return 0;
return slot + 1;
}
static inline int prev_slot(struct b43legacy_dmaring *ring, int slot)
{
B43legacy_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1));
if (slot == 0)
return ring->nr_slots - 1;
return slot - 1;
}
#ifdef CONFIG_B43LEGACY_DEBUG
static void update_max_used_slots(struct b43legacy_dmaring *ring,
int current_used_slots)
{
if (current_used_slots <= ring->max_used_slots)
return;
ring->max_used_slots = current_used_slots;
if (b43legacy_debug(ring->dev, B43legacy_DBG_DMAVERBOSE))
b43legacydbg(ring->dev->wl,
"max_used_slots increased to %d on %s ring %d\n",
ring->max_used_slots,
ring->tx ? "TX" : "RX",
ring->index);
}
#else
static inline
void update_max_used_slots(struct b43legacy_dmaring *ring,
int current_used_slots)
{ }
#endif /* DEBUG */
/* Request a slot for usage. */
static inline
int request_slot(struct b43legacy_dmaring *ring)
{
int slot;
B43legacy_WARN_ON(!ring->tx);
B43legacy_WARN_ON(ring->stopped);
B43legacy_WARN_ON(free_slots(ring) == 0);
slot = next_slot(ring, ring->current_slot);
ring->current_slot = slot;
ring->used_slots++;
update_max_used_slots(ring, ring->used_slots);
return slot;
}
/* Mac80211-queue to b43legacy-ring mapping */
static struct b43legacy_dmaring *priority_to_txring(
struct b43legacy_wldev *dev,
int queue_priority)
{
struct b43legacy_dmaring *ring;
/*FIXME: For now we always run on TX-ring-1 */
return dev->dma.tx_ring1;
/* 0 = highest priority */
switch (queue_priority) {
default:
B43legacy_WARN_ON(1);
/* fallthrough */
case 0:
ring = dev->dma.tx_ring3;
break;
case 1:
ring = dev->dma.tx_ring2;
break;
case 2:
ring = dev->dma.tx_ring1;
break;
case 3:
ring = dev->dma.tx_ring0;
break;
case 4:
ring = dev->dma.tx_ring4;
break;
case 5:
ring = dev->dma.tx_ring5;
break;
}
return ring;
}
/* Bcm4301-ring to mac80211-queue mapping */
static inline int txring_to_priority(struct b43legacy_dmaring *ring)
{
static const u8 idx_to_prio[] =
{ 3, 2, 1, 0, 4, 5, };
/*FIXME: have only one queue, for now */
return 0;
return idx_to_prio[ring->index];
}
u16 b43legacy_dmacontroller_base(int dma64bit, int controller_idx)
{
static const u16 map64[] = {
B43legacy_MMIO_DMA64_BASE0,
B43legacy_MMIO_DMA64_BASE1,
B43legacy_MMIO_DMA64_BASE2,
B43legacy_MMIO_DMA64_BASE3,
B43legacy_MMIO_DMA64_BASE4,
B43legacy_MMIO_DMA64_BASE5,
};
static const u16 map32[] = {
B43legacy_MMIO_DMA32_BASE0,
B43legacy_MMIO_DMA32_BASE1,
B43legacy_MMIO_DMA32_BASE2,
B43legacy_MMIO_DMA32_BASE3,
B43legacy_MMIO_DMA32_BASE4,
B43legacy_MMIO_DMA32_BASE5,
};
if (dma64bit) {
B43legacy_WARN_ON(!(controller_idx >= 0 &&
controller_idx < ARRAY_SIZE(map64)));
return map64[controller_idx];
}
B43legacy_WARN_ON(!(controller_idx >= 0 &&
controller_idx < ARRAY_SIZE(map32)));
return map32[controller_idx];
}
static inline
dma_addr_t map_descbuffer(struct b43legacy_dmaring *ring,
unsigned char *buf,
size_t len,
int tx)
{
dma_addr_t dmaaddr;
if (tx)
dmaaddr = dma_map_single(ring->dev->dev->dev,
buf, len,
DMA_TO_DEVICE);
else
dmaaddr = dma_map_single(ring->dev->dev->dev,
buf, len,
DMA_FROM_DEVICE);
return dmaaddr;
}
static inline
void unmap_descbuffer(struct b43legacy_dmaring *ring,
dma_addr_t addr,
size_t len,
int tx)
{
if (tx)
dma_unmap_single(ring->dev->dev->dev,
addr, len,
DMA_TO_DEVICE);
else
dma_unmap_single(ring->dev->dev->dev,
addr, len,
DMA_FROM_DEVICE);
}
static inline
void sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring,
dma_addr_t addr,
size_t len)
{
B43legacy_WARN_ON(ring->tx);
dma_sync_single_for_cpu(ring->dev->dev->dev,
addr, len, DMA_FROM_DEVICE);
}
static inline
void sync_descbuffer_for_device(struct b43legacy_dmaring *ring,
dma_addr_t addr,
size_t len)
{
B43legacy_WARN_ON(ring->tx);
dma_sync_single_for_device(ring->dev->dev->dev,
addr, len, DMA_FROM_DEVICE);
}
static inline
void free_descriptor_buffer(struct b43legacy_dmaring *ring,
struct b43legacy_dmadesc_meta *meta,
int irq_context)
{
if (meta->skb) {
if (irq_context)
dev_kfree_skb_irq(meta->skb);
else
dev_kfree_skb(meta->skb);
meta->skb = NULL;
}
}
static int alloc_ringmemory(struct b43legacy_dmaring *ring)
{
struct device *dev = ring->dev->dev->dev;
ring->descbase = dma_alloc_coherent(dev, B43legacy_DMA_RINGMEMSIZE,
&(ring->dmabase), GFP_KERNEL);
if (!ring->descbase) {
b43legacyerr(ring->dev->wl, "DMA ringmemory allocation"
" failed\n");
return -ENOMEM;
}
memset(ring->descbase, 0, B43legacy_DMA_RINGMEMSIZE);
return 0;
}
static void free_ringmemory(struct b43legacy_dmaring *ring)
{
struct device *dev = ring->dev->dev->dev;
dma_free_coherent(dev, B43legacy_DMA_RINGMEMSIZE,
ring->descbase, ring->dmabase);
}
/* Reset the RX DMA channel */
int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev,
u16 mmio_base, int dma64)
{
int i;
u32 value;
u16 offset;
might_sleep();
offset = dma64 ? B43legacy_DMA64_RXCTL : B43legacy_DMA32_RXCTL;
b43legacy_write32(dev, mmio_base + offset, 0);
for (i = 0; i < 10; i++) {
offset = dma64 ? B43legacy_DMA64_RXSTATUS :
B43legacy_DMA32_RXSTATUS;
value = b43legacy_read32(dev, mmio_base + offset);
if (dma64) {
value &= B43legacy_DMA64_RXSTAT;
if (value == B43legacy_DMA64_RXSTAT_DISABLED) {
i = -1;
break;
}
} else {
value &= B43legacy_DMA32_RXSTATE;
if (value == B43legacy_DMA32_RXSTAT_DISABLED) {
i = -1;
break;
}
}
msleep(1);
}
if (i != -1) {
b43legacyerr(dev->wl, "DMA RX reset timed out\n");
return -ENODEV;
}
return 0;
}
/* Reset the RX DMA channel */
int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev,
u16 mmio_base, int dma64)
{
int i;
u32 value;
u16 offset;
might_sleep();
for (i = 0; i < 10; i++) {
offset = dma64 ? B43legacy_DMA64_TXSTATUS :
B43legacy_DMA32_TXSTATUS;
value = b43legacy_read32(dev, mmio_base + offset);
if (dma64) {
value &= B43legacy_DMA64_TXSTAT;
if (value == B43legacy_DMA64_TXSTAT_DISABLED ||
value == B43legacy_DMA64_TXSTAT_IDLEWAIT ||
value == B43legacy_DMA64_TXSTAT_STOPPED)
break;
} else {
value &= B43legacy_DMA32_TXSTATE;
if (value == B43legacy_DMA32_TXSTAT_DISABLED ||
value == B43legacy_DMA32_TXSTAT_IDLEWAIT ||
value == B43legacy_DMA32_TXSTAT_STOPPED)
break;
}
msleep(1);
}
offset = dma64 ? B43legacy_DMA64_TXCTL : B43legacy_DMA32_TXCTL;
b43legacy_write32(dev, mmio_base + offset, 0);
for (i = 0; i < 10; i++) {
offset = dma64 ? B43legacy_DMA64_TXSTATUS :
B43legacy_DMA32_TXSTATUS;
value = b43legacy_read32(dev, mmio_base + offset);
if (dma64) {
value &= B43legacy_DMA64_TXSTAT;
if (value == B43legacy_DMA64_TXSTAT_DISABLED) {
i = -1;
break;
}
} else {
value &= B43legacy_DMA32_TXSTATE;
if (value == B43legacy_DMA32_TXSTAT_DISABLED) {
i = -1;
break;
}
}
msleep(1);
}
if (i != -1) {
b43legacyerr(dev->wl, "DMA TX reset timed out\n");
return -ENODEV;
}
/* ensure the reset is completed. */
msleep(1);
return 0;
}
static int setup_rx_descbuffer(struct b43legacy_dmaring *ring,
struct b43legacy_dmadesc_generic *desc,
struct b43legacy_dmadesc_meta *meta,
gfp_t gfp_flags)
{
struct b43legacy_rxhdr_fw3 *rxhdr;
struct b43legacy_hwtxstatus *txstat;
dma_addr_t dmaaddr;
struct sk_buff *skb;
B43legacy_WARN_ON(ring->tx);
skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
if (unlikely(!skb))
return -ENOMEM;
dmaaddr = map_descbuffer(ring, skb->data,
ring->rx_buffersize, 0);
if (dma_mapping_error(dmaaddr)) {
/* ugh. try to realloc in zone_dma */
gfp_flags |= GFP_DMA;
dev_kfree_skb_any(skb);
skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
if (unlikely(!skb))
return -ENOMEM;
dmaaddr = map_descbuffer(ring, skb->data,
ring->rx_buffersize, 0);
}
if (dma_mapping_error(dmaaddr)) {
dev_kfree_skb_any(skb);
return -EIO;
}
meta->skb = skb;
meta->dmaaddr = dmaaddr;
ring->ops->fill_descriptor(ring, desc, dmaaddr,
ring->rx_buffersize, 0, 0, 0);
rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data);
rxhdr->frame_len = 0;
txstat = (struct b43legacy_hwtxstatus *)(skb->data);
txstat->cookie = 0;
return 0;
}
/* Allocate the initial descbuffers.
* This is used for an RX ring only.
*/
static int alloc_initial_descbuffers(struct b43legacy_dmaring *ring)
{
int i;
int err = -ENOMEM;
struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
for (i = 0; i < ring->nr_slots; i++) {
desc = ring->ops->idx2desc(ring, i, &meta);
err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL);
if (err) {
b43legacyerr(ring->dev->wl,
"Failed to allocate initial descbuffers\n");
goto err_unwind;
}
}
mb(); /* all descbuffer setup before next line */
ring->used_slots = ring->nr_slots;
err = 0;
out:
return err;
err_unwind:
for (i--; i >= 0; i--) {
desc = ring->ops->idx2desc(ring, i, &meta);
unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0);
dev_kfree_skb(meta->skb);
}
goto out;
}
/* Do initial setup of the DMA controller.
* Reset the controller, write the ring busaddress
* and switch the "enable" bit on.
*/
static int dmacontroller_setup(struct b43legacy_dmaring *ring)
{
int err = 0;
u32 value;
u32 addrext;
u32 trans = ssb_dma_translation(ring->dev->dev);
if (ring->tx) {
if (ring->dma64) {
u64 ringbase = (u64)(ring->dmabase);
addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
value = B43legacy_DMA64_TXENABLE;
value |= (addrext << B43legacy_DMA64_TXADDREXT_SHIFT)
& B43legacy_DMA64_TXADDREXT_MASK;
b43legacy_dma_write(ring, B43legacy_DMA64_TXCTL,
value);
b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGLO,
(ringbase & 0xFFFFFFFF));
b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGHI,
((ringbase >> 32)
& ~SSB_DMA_TRANSLATION_MASK)
| trans);
} else {
u32 ringbase = (u32)(ring->dmabase);
addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
value = B43legacy_DMA32_TXENABLE;
value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT)
& B43legacy_DMA32_TXADDREXT_MASK;
b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL,
value);
b43legacy_dma_write(ring, B43legacy_DMA32_TXRING,
(ringbase &
~SSB_DMA_TRANSLATION_MASK)
| trans);
}
} else {
err = alloc_initial_descbuffers(ring);
if (err)
goto out;
if (ring->dma64) {
u64 ringbase = (u64)(ring->dmabase);
addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
value = (ring->frameoffset <<
B43legacy_DMA64_RXFROFF_SHIFT);
value |= B43legacy_DMA64_RXENABLE;
value |= (addrext << B43legacy_DMA64_RXADDREXT_SHIFT)
& B43legacy_DMA64_RXADDREXT_MASK;
b43legacy_dma_write(ring, B43legacy_DMA64_RXCTL,
value);
b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGLO,
(ringbase & 0xFFFFFFFF));
b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGHI,
((ringbase >> 32) &
~SSB_DMA_TRANSLATION_MASK) |
trans);
b43legacy_dma_write(ring, B43legacy_DMA64_RXINDEX,
200);
} else {
u32 ringbase = (u32)(ring->dmabase);
addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
>> SSB_DMA_TRANSLATION_SHIFT;
value = (ring->frameoffset <<
B43legacy_DMA32_RXFROFF_SHIFT);
value |= B43legacy_DMA32_RXENABLE;
value |= (addrext <<
B43legacy_DMA32_RXADDREXT_SHIFT)
& B43legacy_DMA32_RXADDREXT_MASK;
b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL,
value);
b43legacy_dma_write(ring, B43legacy_DMA32_RXRING,
(ringbase &
~SSB_DMA_TRANSLATION_MASK)
| trans);
b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX,
200);
}
}
out:
return err;
}
/* Shutdown the DMA controller. */
static void dmacontroller_cleanup(struct b43legacy_dmaring *ring)
{
if (ring->tx) {
b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base,
ring->dma64);
if (ring->dma64) {
b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGLO, 0);
b43legacy_dma_write(ring, B43legacy_DMA64_TXRINGHI, 0);
} else
b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0);
} else {
b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base,
ring->dma64);
if (ring->dma64) {
b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGLO, 0);
b43legacy_dma_write(ring, B43legacy_DMA64_RXRINGHI, 0);
} else
b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0);
}
}
static void free_all_descbuffers(struct b43legacy_dmaring *ring)
{
struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
int i;
if (!ring->used_slots)
return;
for (i = 0; i < ring->nr_slots; i++) {
desc = ring->ops->idx2desc(ring, i, &meta);
if (!meta->skb) {
B43legacy_WARN_ON(!ring->tx);
continue;
}
if (ring->tx)
unmap_descbuffer(ring, meta->dmaaddr,
meta->skb->len, 1);
else
unmap_descbuffer(ring, meta->dmaaddr,
ring->rx_buffersize, 0);
free_descriptor_buffer(ring, meta, 0);
}
}
static u64 supported_dma_mask(struct b43legacy_wldev *dev)
{
u32 tmp;
u16 mmio_base;
tmp = b43legacy_read32(dev, SSB_TMSHIGH);
if (tmp & SSB_TMSHIGH_DMA64)
return DMA_64BIT_MASK;
mmio_base = b43legacy_dmacontroller_base(0, 0);
b43legacy_write32(dev,
mmio_base + B43legacy_DMA32_TXCTL,
B43legacy_DMA32_TXADDREXT_MASK);
tmp = b43legacy_read32(dev, mmio_base +
B43legacy_DMA32_TXCTL);
if (tmp & B43legacy_DMA32_TXADDREXT_MASK)
return DMA_32BIT_MASK;
return DMA_30BIT_MASK;
}
/* Main initialization function. */
static
struct b43legacy_dmaring *b43legacy_setup_dmaring(
struct b43legacy_wldev *dev,
int controller_index,
int for_tx,
int dma64)
{
struct b43legacy_dmaring *ring;
int err;
int nr_slots;
dma_addr_t dma_test;
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
if (!ring)
goto out;
nr_slots = B43legacy_RXRING_SLOTS;
if (for_tx)
nr_slots = B43legacy_TXRING_SLOTS;
ring->meta = kcalloc(nr_slots, sizeof(struct b43legacy_dmadesc_meta),
GFP_KERNEL);
if (!ring->meta)
goto err_kfree_ring;
if (for_tx) {
ring->txhdr_cache = kcalloc(nr_slots,
sizeof(struct b43legacy_txhdr_fw3),
GFP_KERNEL);
if (!ring->txhdr_cache)
goto err_kfree_meta;
/* test for ability to dma to txhdr_cache */
dma_test = dma_map_single(dev->dev->dev,
ring->txhdr_cache,
sizeof(struct b43legacy_txhdr_fw3),
DMA_TO_DEVICE);
if (dma_mapping_error(dma_test)) {
/* ugh realloc */
kfree(ring->txhdr_cache);
ring->txhdr_cache = kcalloc(nr_slots,
sizeof(struct b43legacy_txhdr_fw3),
GFP_KERNEL | GFP_DMA);
if (!ring->txhdr_cache)
goto err_kfree_meta;
dma_test = dma_map_single(dev->dev->dev,
ring->txhdr_cache,
sizeof(struct b43legacy_txhdr_fw3),
DMA_TO_DEVICE);
if (dma_mapping_error(dma_test))
goto err_kfree_txhdr_cache;
}
dma_unmap_single(dev->dev->dev,
dma_test, sizeof(struct b43legacy_txhdr_fw3),
DMA_TO_DEVICE);
}
ring->dev = dev;
ring->nr_slots = nr_slots;
ring->mmio_base = b43legacy_dmacontroller_base(dma64,
controller_index);
ring->index = controller_index;
ring->dma64 = !!dma64;
if (dma64)
ring->ops = &dma64_ops;
else
ring->ops = &dma32_ops;
if (for_tx) {
ring->tx = 1;
ring->current_slot = -1;
} else {
if (ring->index == 0) {
ring->rx_buffersize = B43legacy_DMA0_RX_BUFFERSIZE;
ring->frameoffset = B43legacy_DMA0_RX_FRAMEOFFSET;
} else if (ring->index == 3) {
ring->rx_buffersize = B43legacy_DMA3_RX_BUFFERSIZE;
ring->frameoffset = B43legacy_DMA3_RX_FRAMEOFFSET;
} else
B43legacy_WARN_ON(1);
}
spin_lock_init(&ring->lock);
#ifdef CONFIG_B43LEGACY_DEBUG
ring->last_injected_overflow = jiffies;
#endif
err = alloc_ringmemory(ring);
if (err)
goto err_kfree_txhdr_cache;
err = dmacontroller_setup(ring);
if (err)
goto err_free_ringmemory;
out:
return ring;
err_free_ringmemory:
free_ringmemory(ring);
err_kfree_txhdr_cache:
kfree(ring->txhdr_cache);
err_kfree_meta:
kfree(ring->meta);
err_kfree_ring:
kfree(ring);
ring = NULL;
goto out;
}
/* Main cleanup function. */
static void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring)
{
if (!ring)
return;
b43legacydbg(ring->dev->wl, "DMA-%s 0x%04X (%s) max used slots:"
" %d/%d\n", (ring->dma64) ? "64" : "32", ring->mmio_base,
(ring->tx) ? "TX" : "RX",
ring->max_used_slots, ring->nr_slots);
/* Device IRQs are disabled prior entering this function,
* so no need to take care of concurrency with rx handler stuff.
*/
dmacontroller_cleanup(ring);
free_all_descbuffers(ring);
free_ringmemory(ring);
kfree(ring->txhdr_cache);
kfree(ring->meta);
kfree(ring);
}
void b43legacy_dma_free(struct b43legacy_wldev *dev)
{
struct b43legacy_dma *dma;
if (b43legacy_using_pio(dev))
return;
dma = &dev->dma;
b43legacy_destroy_dmaring(dma->rx_ring3);
dma->rx_ring3 = NULL;
b43legacy_destroy_dmaring(dma->rx_ring0);
dma->rx_ring0 = NULL;
b43legacy_destroy_dmaring(dma->tx_ring5);
dma->tx_ring5 = NULL;
b43legacy_destroy_dmaring(dma->tx_ring4);
dma->tx_ring4 = NULL;
b43legacy_destroy_dmaring(dma->tx_ring3);
dma->tx_ring3 = NULL;
b43legacy_destroy_dmaring(dma->tx_ring2);
dma->tx_ring2 = NULL;
b43legacy_destroy_dmaring(dma->tx_ring1);
dma->tx_ring1 = NULL;
b43legacy_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL;
}
int b43legacy_dma_init(struct b43legacy_wldev *dev)
{
struct b43legacy_dma *dma = &dev->dma;
struct b43legacy_dmaring *ring;
int err;
u64 dmamask;
int dma64 = 0;
dmamask = supported_dma_mask(dev);
if (dmamask == DMA_64BIT_MASK)
dma64 = 1;
err = ssb_dma_set_mask(dev->dev, dmamask);
if (err) {
#ifdef BCM43XX_PIO
b43legacywarn(dev->wl, "DMA for this device not supported. "
"Falling back to PIO\n");
dev->__using_pio = 1;
return -EAGAIN;
#else
b43legacyerr(dev->wl, "DMA for this device not supported and "
"no PIO support compiled in\n");
return -EOPNOTSUPP;
#endif
}
err = -ENOMEM;
/* setup TX DMA channels. */
ring = b43legacy_setup_dmaring(dev, 0, 1, dma64);
if (!ring)
goto out;
dma->tx_ring0 = ring;
ring = b43legacy_setup_dmaring(dev, 1, 1, dma64);
if (!ring)
goto err_destroy_tx0;
dma->tx_ring1 = ring;
ring = b43legacy_setup_dmaring(dev, 2, 1, dma64);
if (!ring)
goto err_destroy_tx1;
dma->tx_ring2 = ring;
ring = b43legacy_setup_dmaring(dev, 3, 1, dma64);
if (!ring)
goto err_destroy_tx2;
dma->tx_ring3 = ring;
ring = b43legacy_setup_dmaring(dev, 4, 1, dma64);
if (!ring)
goto err_destroy_tx3;
dma->tx_ring4 = ring;
ring = b43legacy_setup_dmaring(dev, 5, 1, dma64);
if (!ring)
goto err_destroy_tx4;
dma->tx_ring5 = ring;
/* setup RX DMA channels. */
ring = b43legacy_setup_dmaring(dev, 0, 0, dma64);
if (!ring)
goto err_destroy_tx5;
dma->rx_ring0 = ring;
if (dev->dev->id.revision < 5) {
ring = b43legacy_setup_dmaring(dev, 3, 0, dma64);
if (!ring)
goto err_destroy_rx0;
dma->rx_ring3 = ring;
}
b43legacydbg(dev->wl, "%d-bit DMA initialized\n",
(dmamask == DMA_64BIT_MASK) ? 64 :
(dmamask == DMA_32BIT_MASK) ? 32 : 30);
err = 0;
out:
return err;
err_destroy_rx0:
b43legacy_destroy_dmaring(dma->rx_ring0);
dma->rx_ring0 = NULL;
err_destroy_tx5:
b43legacy_destroy_dmaring(dma->tx_ring5);
dma->tx_ring5 = NULL;
err_destroy_tx4:
b43legacy_destroy_dmaring(dma->tx_ring4);
dma->tx_ring4 = NULL;
err_destroy_tx3:
b43legacy_destroy_dmaring(dma->tx_ring3);
dma->tx_ring3 = NULL;
err_destroy_tx2:
b43legacy_destroy_dmaring(dma->tx_ring2);
dma->tx_ring2 = NULL;
err_destroy_tx1:
b43legacy_destroy_dmaring(dma->tx_ring1);
dma->tx_ring1 = NULL;
err_destroy_tx0:
b43legacy_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL;
goto out;
}
/* Generate a cookie for the TX header. */
static u16 generate_cookie(struct b43legacy_dmaring *ring,
int slot)
{
u16 cookie = 0x1000;
/* Use the upper 4 bits of the cookie as
* DMA controller ID and store the slot number
* in the lower 12 bits.
* Note that the cookie must never be 0, as this
* is a special value used in RX path.
*/
switch (ring->index) {
case 0:
cookie = 0xA000;
break;
case 1:
cookie = 0xB000;
break;
case 2:
cookie = 0xC000;
break;
case 3:
cookie = 0xD000;
break;
case 4:
cookie = 0xE000;
break;
case 5:
cookie = 0xF000;
break;
}
B43legacy_WARN_ON(!(((u16)slot & 0xF000) == 0x0000));
cookie |= (u16)slot;
return cookie;
}
/* Inspect a cookie and find out to which controller/slot it belongs. */
static
struct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev,
u16 cookie, int *slot)
{
struct b43legacy_dma *dma = &dev->dma;
struct b43legacy_dmaring *ring = NULL;
switch (cookie & 0xF000) {
case 0xA000:
ring = dma->tx_ring0;
break;
case 0xB000:
ring = dma->tx_ring1;
break;
case 0xC000:
ring = dma->tx_ring2;
break;
case 0xD000:
ring = dma->tx_ring3;
break;
case 0xE000:
ring = dma->tx_ring4;
break;
case 0xF000:
ring = dma->tx_ring5;
break;
default:
B43legacy_WARN_ON(1);
}
*slot = (cookie & 0x0FFF);
B43legacy_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots));
return ring;
}
static int dma_tx_fragment(struct b43legacy_dmaring *ring,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
const struct b43legacy_dma_ops *ops = ring->ops;
u8 *header;
int slot;
int err;
struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
struct b43legacy_dmadesc_meta *meta_hdr;
struct sk_buff *bounce_skb;
#define SLOTS_PER_PACKET 2
B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0);
/* Get a slot for the header. */
slot = request_slot(ring);
desc = ops->idx2desc(ring, slot, &meta_hdr);
memset(meta_hdr, 0, sizeof(*meta_hdr));
header = &(ring->txhdr_cache[slot * sizeof(
struct b43legacy_txhdr_fw3)]);
b43legacy_generate_txhdr(ring->dev, header,
skb->data, skb->len, ctl,
generate_cookie(ring, slot));
meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header,
sizeof(struct b43legacy_txhdr_fw3), 1);
if (dma_mapping_error(meta_hdr->dmaaddr))
return -EIO;
ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr,
sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0);
/* Get a slot for the payload. */
slot = request_slot(ring);
desc = ops->idx2desc(ring, slot, &meta);
memset(meta, 0, sizeof(*meta));
memcpy(&meta->txstat.control, ctl, sizeof(*ctl));
meta->skb = skb;
meta->is_last_fragment = 1;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
/* create a bounce buffer in zone_dma on mapping failure. */
if (dma_mapping_error(meta->dmaaddr)) {
bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
if (!bounce_skb) {
err = -ENOMEM;
goto out_unmap_hdr;
}
memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
dev_kfree_skb_any(skb);
skb = bounce_skb;
meta->skb = skb;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
if (dma_mapping_error(meta->dmaaddr)) {
err = -EIO;
goto out_free_bounce;
}
}
ops->fill_descriptor(ring, desc, meta->dmaaddr,
skb->len, 0, 1, 1);
wmb(); /* previous stuff MUST be done */
/* Now transfer the whole frame. */
ops->poke_tx(ring, next_slot(ring, slot));
return 0;
out_free_bounce:
dev_kfree_skb_any(skb);
out_unmap_hdr:
unmap_descbuffer(ring, meta_hdr->dmaaddr,
sizeof(struct b43legacy_txhdr_fw3), 1);
return err;
}
static inline
int should_inject_overflow(struct b43legacy_dmaring *ring)
{
#ifdef CONFIG_B43LEGACY_DEBUG
if (unlikely(b43legacy_debug(ring->dev,
B43legacy_DBG_DMAOVERFLOW))) {
/* Check if we should inject another ringbuffer overflow
* to test handling of this situation in the stack. */
unsigned long next_overflow;
next_overflow = ring->last_injected_overflow + HZ;
if (time_after(jiffies, next_overflow)) {
ring->last_injected_overflow = jiffies;
b43legacydbg(ring->dev->wl,
"Injecting TX ring overflow on "
"DMA controller %d\n", ring->index);
return 1;
}
}
#endif /* CONFIG_B43LEGACY_DEBUG */
return 0;
}
int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
struct b43legacy_dmaring *ring;
int err = 0;
unsigned long flags;
ring = priority_to_txring(dev, ctl->queue);
spin_lock_irqsave(&ring->lock, flags);
B43legacy_WARN_ON(!ring->tx);
if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) {
b43legacywarn(dev->wl, "DMA queue overflow\n");
err = -ENOSPC;
goto out_unlock;
}
/* Check if the queue was stopped in mac80211,
* but we got called nevertheless.
* That would be a mac80211 bug. */
B43legacy_BUG_ON(ring->stopped);
err = dma_tx_fragment(ring, skb, ctl);
if (unlikely(err)) {
b43legacyerr(dev->wl, "DMA tx mapping failure\n");
goto out_unlock;
}
ring->nr_tx_packets++;
if ((free_slots(ring) < SLOTS_PER_PACKET) ||
should_inject_overflow(ring)) {
/* This TX ring is full. */
ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring));
ring->stopped = 1;
if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE))
b43legacydbg(dev->wl, "Stopped TX ring %d\n",
ring->index);
}
out_unlock:
spin_unlock_irqrestore(&ring->lock, flags);
return err;
}
void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
const struct b43legacy_dma_ops *ops;
struct b43legacy_dmaring *ring;
struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
int slot;
ring = parse_cookie(dev, status->cookie, &slot);
if (unlikely(!ring))
return;
B43legacy_WARN_ON(!irqs_disabled());
spin_lock(&ring->lock);
B43legacy_WARN_ON(!ring->tx);
ops = ring->ops;
while (1) {
B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
desc = ops->idx2desc(ring, slot, &meta);
if (meta->skb)
unmap_descbuffer(ring, meta->dmaaddr,
meta->skb->len, 1);
else
unmap_descbuffer(ring, meta->dmaaddr,
sizeof(struct b43legacy_txhdr_fw3),
1);
if (meta->is_last_fragment) {
B43legacy_WARN_ON(!meta->skb);
/* Call back to inform the ieee80211 subsystem about the
* status of the transmission.
* Some fields of txstat are already filled in dma_tx().
*/
if (status->acked) {
meta->txstat.flags |= IEEE80211_TX_STATUS_ACK;
} else {
if (!(meta->txstat.control.flags
& IEEE80211_TXCTL_NO_ACK))
meta->txstat.excessive_retries = 1;
}
if (status->frame_count == 0) {
/* The frame was not transmitted at all. */
meta->txstat.retry_count = 0;
} else
meta->txstat.retry_count = status->frame_count
- 1;
ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
&(meta->txstat));
/* skb is freed by ieee80211_tx_status_irqsafe() */
meta->skb = NULL;
} else {
/* No need to call free_descriptor_buffer here, as
* this is only the txhdr, which is not allocated.
*/
B43legacy_WARN_ON(meta->skb != NULL);
}
/* Everything unmapped and free'd. So it's not used anymore. */
ring->used_slots--;
if (meta->is_last_fragment)
break;
slot = next_slot(ring, slot);
}
dev->stats.last_tx = jiffies;
if (ring->stopped) {
B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
ring->stopped = 0;
if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE))
b43legacydbg(dev->wl, "Woke up TX ring %d\n",
ring->index);
}
spin_unlock(&ring->lock);
}
void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
const int nr_queues = dev->wl->hw->queues;
struct b43legacy_dmaring *ring;
struct ieee80211_tx_queue_stats_data *data;
unsigned long flags;
int i;
for (i = 0; i < nr_queues; i++) {
data = &(stats->data[i]);
ring = priority_to_txring(dev, i);
spin_lock_irqsave(&ring->lock, flags);
data->len = ring->used_slots / SLOTS_PER_PACKET;
data->limit = ring->nr_slots / SLOTS_PER_PACKET;
data->count = ring->nr_tx_packets;
spin_unlock_irqrestore(&ring->lock, flags);
}
}
static void dma_rx(struct b43legacy_dmaring *ring,
int *slot)
{
const struct b43legacy_dma_ops *ops = ring->ops;
struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
struct b43legacy_rxhdr_fw3 *rxhdr;
struct sk_buff *skb;
u16 len;
int err;
dma_addr_t dmaaddr;
desc = ops->idx2desc(ring, *slot, &meta);
sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
skb = meta->skb;
if (ring->index == 3) {
/* We received an xmit status. */
struct b43legacy_hwtxstatus *hw =
(struct b43legacy_hwtxstatus *)skb->data;
int i = 0;
while (hw->cookie == 0) {
if (i > 100)
break;
i++;
udelay(2);
barrier();
}
b43legacy_handle_hwtxstatus(ring->dev, hw);
/* recycle the descriptor buffer. */
sync_descbuffer_for_device(ring, meta->dmaaddr,
ring->rx_buffersize);
return;
}
rxhdr = (struct b43legacy_rxhdr_fw3 *)skb->data;
len = le16_to_cpu(rxhdr->frame_len);
if (len == 0) {
int i = 0;
do {
udelay(2);
barrier();
len = le16_to_cpu(rxhdr->frame_len);
} while (len == 0 && i++ < 5);
if (unlikely(len == 0)) {
/* recycle the descriptor buffer. */
sync_descbuffer_for_device(ring, meta->dmaaddr,
ring->rx_buffersize);
goto drop;
}
}
if (unlikely(len > ring->rx_buffersize)) {
/* The data did not fit into one descriptor buffer
* and is split over multiple buffers.
* This should never happen, as we try to allocate buffers
* big enough. So simply ignore this packet.
*/
int cnt = 0;
s32 tmp = len;
while (1) {
desc = ops->idx2desc(ring, *slot, &meta);
/* recycle the descriptor buffer. */
sync_descbuffer_for_device(ring, meta->dmaaddr,
ring->rx_buffersize);
*slot = next_slot(ring, *slot);
cnt++;
tmp -= ring->rx_buffersize;
if (tmp <= 0)
break;
}
b43legacyerr(ring->dev->wl, "DMA RX buffer too small "
"(len: %u, buffer: %u, nr-dropped: %d)\n",
len, ring->rx_buffersize, cnt);
goto drop;
}
dmaaddr = meta->dmaaddr;
err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC);
if (unlikely(err)) {
b43legacydbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer()"
" failed\n");
sync_descbuffer_for_device(ring, dmaaddr,
ring->rx_buffersize);
goto drop;
}
unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);
skb_put(skb, len + ring->frameoffset);
skb_pull(skb, ring->frameoffset);
b43legacy_rx(ring->dev, skb, rxhdr);
drop:
return;
}
void b43legacy_dma_rx(struct b43legacy_dmaring *ring)
{
const struct b43legacy_dma_ops *ops = ring->ops;
int slot;
int current_slot;
int used_slots = 0;
B43legacy_WARN_ON(ring->tx);
current_slot = ops->get_current_rxslot(ring);
B43legacy_WARN_ON(!(current_slot >= 0 && current_slot <
ring->nr_slots));
slot = ring->current_slot;
for (; slot != current_slot; slot = next_slot(ring, slot)) {
dma_rx(ring, &slot);
update_max_used_slots(ring, ++used_slots);
}
ops->set_current_rxslot(ring, slot);
ring->current_slot = slot;
}
static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring)
{
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
B43legacy_WARN_ON(!ring->tx);
ring->ops->tx_suspend(ring);
spin_unlock_irqrestore(&ring->lock, flags);
}
static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring)
{
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
B43legacy_WARN_ON(!ring->tx);
ring->ops->tx_resume(ring);
spin_unlock_irqrestore(&ring->lock, flags);
}
void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev)
{
b43legacy_power_saving_ctl_bits(dev, -1, 1);
b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring0);
b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring1);
b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring2);
b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring3);
b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring4);
b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring5);
}
void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev)
{
b43legacy_dma_tx_resume_ring(dev->dma.tx_ring5);
b43legacy_dma_tx_resume_ring(dev->dma.tx_ring4);
b43legacy_dma_tx_resume_ring(dev->dma.tx_ring3);
b43legacy_dma_tx_resume_ring(dev->dma.tx_ring2);
b43legacy_dma_tx_resume_ring(dev->dma.tx_ring1);
b43legacy_dma_tx_resume_ring(dev->dma.tx_ring0);
b43legacy_power_saving_ctl_bits(dev, -1, -1);
}
#ifndef B43legacy_DMA_H_
#define B43legacy_DMA_H_
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/linkage.h>
#include <asm/atomic.h>
#include "b43legacy.h"
/* DMA-Interrupt reasons. */
#define B43legacy_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \
| (1 << 14) | (1 << 15))
#define B43legacy_DMAIRQ_NONFATALMASK (1 << 13)
#define B43legacy_DMAIRQ_RX_DONE (1 << 16)
/*** 32-bit DMA Engine. ***/
/* 32-bit DMA controller registers. */
#define B43legacy_DMA32_TXCTL 0x00
#define B43legacy_DMA32_TXENABLE 0x00000001
#define B43legacy_DMA32_TXSUSPEND 0x00000002
#define B43legacy_DMA32_TXLOOPBACK 0x00000004
#define B43legacy_DMA32_TXFLUSH 0x00000010
#define B43legacy_DMA32_TXADDREXT_MASK 0x00030000
#define B43legacy_DMA32_TXADDREXT_SHIFT 16
#define B43legacy_DMA32_TXRING 0x04
#define B43legacy_DMA32_TXINDEX 0x08
#define B43legacy_DMA32_TXSTATUS 0x0C
#define B43legacy_DMA32_TXDPTR 0x00000FFF
#define B43legacy_DMA32_TXSTATE 0x0000F000
#define B43legacy_DMA32_TXSTAT_DISABLED 0x00000000
#define B43legacy_DMA32_TXSTAT_ACTIVE 0x00001000
#define B43legacy_DMA32_TXSTAT_IDLEWAIT 0x00002000
#define B43legacy_DMA32_TXSTAT_STOPPED 0x00003000
#define B43legacy_DMA32_TXSTAT_SUSP 0x00004000
#define B43legacy_DMA32_TXERROR 0x000F0000
#define B43legacy_DMA32_TXERR_NOERR 0x00000000
#define B43legacy_DMA32_TXERR_PROT 0x00010000
#define B43legacy_DMA32_TXERR_UNDERRUN 0x00020000
#define B43legacy_DMA32_TXERR_BUFREAD 0x00030000
#define B43legacy_DMA32_TXERR_DESCREAD 0x00040000
#define B43legacy_DMA32_TXACTIVE 0xFFF00000
#define B43legacy_DMA32_RXCTL 0x10
#define B43legacy_DMA32_RXENABLE 0x00000001
#define B43legacy_DMA32_RXFROFF_MASK 0x000000FE
#define B43legacy_DMA32_RXFROFF_SHIFT 1
#define B43legacy_DMA32_RXDIRECTFIFO 0x00000100
#define B43legacy_DMA32_RXADDREXT_MASK 0x00030000
#define B43legacy_DMA32_RXADDREXT_SHIFT 16
#define B43legacy_DMA32_RXRING 0x14
#define B43legacy_DMA32_RXINDEX 0x18
#define B43legacy_DMA32_RXSTATUS 0x1C
#define B43legacy_DMA32_RXDPTR 0x00000FFF
#define B43legacy_DMA32_RXSTATE 0x0000F000
#define B43legacy_DMA32_RXSTAT_DISABLED 0x00000000
#define B43legacy_DMA32_RXSTAT_ACTIVE 0x00001000
#define B43legacy_DMA32_RXSTAT_IDLEWAIT 0x00002000
#define B43legacy_DMA32_RXSTAT_STOPPED 0x00003000
#define B43legacy_DMA32_RXERROR 0x000F0000
#define B43legacy_DMA32_RXERR_NOERR 0x00000000
#define B43legacy_DMA32_RXERR_PROT 0x00010000
#define B43legacy_DMA32_RXERR_OVERFLOW 0x00020000
#define B43legacy_DMA32_RXERR_BUFWRITE 0x00030000
#define B43legacy_DMA32_RXERR_DESCREAD 0x00040000
#define B43legacy_DMA32_RXACTIVE 0xFFF00000
/* 32-bit DMA descriptor. */
struct b43legacy_dmadesc32 {
__le32 control;
__le32 address;
} __attribute__((__packed__));
#define B43legacy_DMA32_DCTL_BYTECNT 0x00001FFF
#define B43legacy_DMA32_DCTL_ADDREXT_MASK 0x00030000
#define B43legacy_DMA32_DCTL_ADDREXT_SHIFT 16
#define B43legacy_DMA32_DCTL_DTABLEEND 0x10000000
#define B43legacy_DMA32_DCTL_IRQ 0x20000000
#define B43legacy_DMA32_DCTL_FRAMEEND 0x40000000
#define B43legacy_DMA32_DCTL_FRAMESTART 0x80000000
/*** 64-bit DMA Engine. ***/
/* 64-bit DMA controller registers. */
#define B43legacy_DMA64_TXCTL 0x00
#define B43legacy_DMA64_TXENABLE 0x00000001
#define B43legacy_DMA64_TXSUSPEND 0x00000002
#define B43legacy_DMA64_TXLOOPBACK 0x00000004
#define B43legacy_DMA64_TXFLUSH 0x00000010
#define B43legacy_DMA64_TXADDREXT_MASK 0x00030000
#define B43legacy_DMA64_TXADDREXT_SHIFT 16
#define B43legacy_DMA64_TXINDEX 0x04
#define B43legacy_DMA64_TXRINGLO 0x08
#define B43legacy_DMA64_TXRINGHI 0x0C
#define B43legacy_DMA64_TXSTATUS 0x10
#define B43legacy_DMA64_TXSTATDPTR 0x00001FFF
#define B43legacy_DMA64_TXSTAT 0xF0000000
#define B43legacy_DMA64_TXSTAT_DISABLED 0x00000000
#define B43legacy_DMA64_TXSTAT_ACTIVE 0x10000000
#define B43legacy_DMA64_TXSTAT_IDLEWAIT 0x20000000
#define B43legacy_DMA64_TXSTAT_STOPPED 0x30000000
#define B43legacy_DMA64_TXSTAT_SUSP 0x40000000
#define B43legacy_DMA64_TXERROR 0x14
#define B43legacy_DMA64_TXERRDPTR 0x0001FFFF
#define B43legacy_DMA64_TXERR 0xF0000000
#define B43legacy_DMA64_TXERR_NOERR 0x00000000
#define B43legacy_DMA64_TXERR_PROT 0x10000000
#define B43legacy_DMA64_TXERR_UNDERRUN 0x20000000
#define B43legacy_DMA64_TXERR_TRANSFER 0x30000000
#define B43legacy_DMA64_TXERR_DESCREAD 0x40000000
#define B43legacy_DMA64_TXERR_CORE 0x50000000
#define B43legacy_DMA64_RXCTL 0x20
#define B43legacy_DMA64_RXENABLE 0x00000001
#define B43legacy_DMA64_RXFROFF_MASK 0x000000FE
#define B43legacy_DMA64_RXFROFF_SHIFT 1
#define B43legacy_DMA64_RXDIRECTFIFO 0x00000100
#define B43legacy_DMA64_RXADDREXT_MASK 0x00030000
#define B43legacy_DMA64_RXADDREXT_SHIFT 16
#define B43legacy_DMA64_RXINDEX 0x24
#define B43legacy_DMA64_RXRINGLO 0x28
#define B43legacy_DMA64_RXRINGHI 0x2C
#define B43legacy_DMA64_RXSTATUS 0x30
#define B43legacy_DMA64_RXSTATDPTR 0x00001FFF
#define B43legacy_DMA64_RXSTAT 0xF0000000
#define B43legacy_DMA64_RXSTAT_DISABLED 0x00000000
#define B43legacy_DMA64_RXSTAT_ACTIVE 0x10000000
#define B43legacy_DMA64_RXSTAT_IDLEWAIT 0x20000000
#define B43legacy_DMA64_RXSTAT_STOPPED 0x30000000
#define B43legacy_DMA64_RXSTAT_SUSP 0x40000000
#define B43legacy_DMA64_RXERROR 0x34
#define B43legacy_DMA64_RXERRDPTR 0x0001FFFF
#define B43legacy_DMA64_RXERR 0xF0000000
#define B43legacy_DMA64_RXERR_NOERR 0x00000000
#define B43legacy_DMA64_RXERR_PROT 0x10000000
#define B43legacy_DMA64_RXERR_UNDERRUN 0x20000000
#define B43legacy_DMA64_RXERR_TRANSFER 0x30000000
#define B43legacy_DMA64_RXERR_DESCREAD 0x40000000
#define B43legacy_DMA64_RXERR_CORE 0x50000000
/* 64-bit DMA descriptor. */
struct b43legacy_dmadesc64 {
__le32 control0;
__le32 control1;
__le32 address_low;
__le32 address_high;
} __attribute__((__packed__));
#define B43legacy_DMA64_DCTL0_DTABLEEND 0x10000000
#define B43legacy_DMA64_DCTL0_IRQ 0x20000000
#define B43legacy_DMA64_DCTL0_FRAMEEND 0x40000000
#define B43legacy_DMA64_DCTL0_FRAMESTART 0x80000000
#define B43legacy_DMA64_DCTL1_BYTECNT 0x00001FFF
#define B43legacy_DMA64_DCTL1_ADDREXT_MASK 0x00030000
#define B43legacy_DMA64_DCTL1_ADDREXT_SHIFT 16
struct b43legacy_dmadesc_generic {
union {
struct b43legacy_dmadesc32 dma32;
struct b43legacy_dmadesc64 dma64;
} __attribute__((__packed__));
} __attribute__((__packed__));
/* Misc DMA constants */
#define B43legacy_DMA_RINGMEMSIZE PAGE_SIZE
#define B43legacy_DMA0_RX_FRAMEOFFSET 30
#define B43legacy_DMA3_RX_FRAMEOFFSET 0
/* DMA engine tuning knobs */
#define B43legacy_TXRING_SLOTS 128
#define B43legacy_RXRING_SLOTS 64
#define B43legacy_DMA0_RX_BUFFERSIZE (2304 + 100)
#define B43legacy_DMA3_RX_BUFFERSIZE 16
#ifdef CONFIG_B43LEGACY_DMA
struct sk_buff;
struct b43legacy_private;
struct b43legacy_txstatus;
struct b43legacy_dmadesc_meta {
/* The kernel DMA-able buffer. */
struct sk_buff *skb;
/* DMA base bus-address of the descriptor buffer. */
dma_addr_t dmaaddr;
/* ieee80211 TX status. Only used once per 802.11 frag. */
bool is_last_fragment;
struct ieee80211_tx_status txstat;
};
struct b43legacy_dmaring;
/* Lowlevel DMA operations that differ between 32bit and 64bit DMA. */
struct b43legacy_dma_ops {
struct b43legacy_dmadesc_generic * (*idx2desc)
(struct b43legacy_dmaring *ring,
int slot,
struct b43legacy_dmadesc_meta
**meta);
void (*fill_descriptor)(struct b43legacy_dmaring *ring,
struct b43legacy_dmadesc_generic *desc,
dma_addr_t dmaaddr, u16 bufsize,
int start, int end, int irq);
void (*poke_tx)(struct b43legacy_dmaring *ring, int slot);
void (*tx_suspend)(struct b43legacy_dmaring *ring);
void (*tx_resume)(struct b43legacy_dmaring *ring);
int (*get_current_rxslot)(struct b43legacy_dmaring *ring);
void (*set_current_rxslot)(struct b43legacy_dmaring *ring, int slot);
};
struct b43legacy_dmaring {
/* Lowlevel DMA ops. */
const struct b43legacy_dma_ops *ops;
/* Kernel virtual base address of the ring memory. */
void *descbase;
/* Meta data about all descriptors. */
struct b43legacy_dmadesc_meta *meta;
/* Cache of TX headers for each slot.
* This is to avoid an allocation on each TX.
* This is NULL for an RX ring.
*/
u8 *txhdr_cache;
/* (Unadjusted) DMA base bus-address of the ring memory. */
dma_addr_t dmabase;
/* Number of descriptor slots in the ring. */
int nr_slots;
/* Number of used descriptor slots. */
int used_slots;
/* Currently used slot in the ring. */
int current_slot;
/* Total number of packets sent. Statistics only. */
unsigned int nr_tx_packets;
/* Frameoffset in octets. */
u32 frameoffset;
/* Descriptor buffer size. */
u16 rx_buffersize;
/* The MMIO base register of the DMA controller. */
u16 mmio_base;
/* DMA controller index number (0-5). */
int index;
/* Boolean. Is this a TX ring? */
bool tx;
/* Boolean. 64bit DMA if true, 32bit DMA otherwise. */
bool dma64;
/* Boolean. Is this ring stopped at ieee80211 level? */
bool stopped;
/* Lock, only used for TX. */
spinlock_t lock;
struct b43legacy_wldev *dev;
#ifdef CONFIG_B43LEGACY_DEBUG
/* Maximum number of used slots. */
int max_used_slots;
/* Last time we injected a ring overflow. */
unsigned long last_injected_overflow;
#endif /* CONFIG_B43LEGACY_DEBUG*/
};
static inline
u32 b43legacy_dma_read(struct b43legacy_dmaring *ring,
u16 offset)
{
return b43legacy_read32(ring->dev, ring->mmio_base + offset);
}
static inline
void b43legacy_dma_write(struct b43legacy_dmaring *ring,
u16 offset, u32 value)
{
b43legacy_write32(ring->dev, ring->mmio_base + offset, value);
}
int b43legacy_dma_init(struct b43legacy_wldev *dev);
void b43legacy_dma_free(struct b43legacy_wldev *dev);
int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev,
u16 dmacontroller_mmio_base,
int dma64);
int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev,
u16 dmacontroller_mmio_base,
int dma64);
u16 b43legacy_dmacontroller_base(int dma64bit, int dmacontroller_idx);
void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev);
void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev);
void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats);
int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl);
void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status);
void b43legacy_dma_rx(struct b43legacy_dmaring *ring);
#else /* CONFIG_B43LEGACY_DMA */
static inline
int b43legacy_dma_init(struct b43legacy_wldev *dev)
{
return 0;
}
static inline
void b43legacy_dma_free(struct b43legacy_wldev *dev)
{
}
static inline
int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev,
u16 dmacontroller_mmio_base,
int dma64)
{
return 0;
}
static inline
int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev,
u16 dmacontroller_mmio_base,
int dma64)
{
return 0;
}
static inline
void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
}
static inline
int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
return 0;
}
static inline
void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
}
static inline
void b43legacy_dma_rx(struct b43legacy_dmaring *ring)
{
}
static inline
void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev)
{
}
static inline
void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev)
{
}
#endif /* CONFIG_B43LEGACY_DMA */
#endif /* B43legacy_DMA_H_ */
/*
Broadcom B43legacy 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>
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 "b43legacy.h"
#include "ilt.h"
#include "phy.h"
/**** Initial Internal Lookup Tables ****/
const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE] = {
0xFEB93FFD, 0xFEC63FFD, /* 0 */
0xFED23FFD, 0xFEDF3FFD,
0xFEEC3FFE, 0xFEF83FFE,
0xFF053FFE, 0xFF113FFE,
0xFF1E3FFE, 0xFF2A3FFF, /* 8 */
0xFF373FFF, 0xFF443FFF,
0xFF503FFF, 0xFF5D3FFF,
0xFF693FFF, 0xFF763FFF,
0xFF824000, 0xFF8F4000, /* 16 */
0xFF9B4000, 0xFFA84000,
0xFFB54000, 0xFFC14000,
0xFFCE4000, 0xFFDA4000,
0xFFE74000, 0xFFF34000, /* 24 */
0x00004000, 0x000D4000,
0x00194000, 0x00264000,
0x00324000, 0x003F4000,
0x004B4000, 0x00584000, /* 32 */
0x00654000, 0x00714000,
0x007E4000, 0x008A3FFF,
0x00973FFF, 0x00A33FFF,
0x00B03FFF, 0x00BC3FFF, /* 40 */
0x00C93FFF, 0x00D63FFF,
0x00E23FFE, 0x00EF3FFE,
0x00FB3FFE, 0x01083FFE,
0x01143FFE, 0x01213FFD, /* 48 */
0x012E3FFD, 0x013A3FFD,
0x01473FFD,
};
const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE] = {
0xDB93CB87, 0xD666CF64, /* 0 */
0xD1FDD358, 0xCDA6D826,
0xCA38DD9F, 0xC729E2B4,
0xC469E88E, 0xC26AEE2B,
0xC0DEF46C, 0xC073FA62, /* 8 */
0xC01D00D5, 0xC0760743,
0xC1560D1E, 0xC2E51369,
0xC4ED18FF, 0xC7AC1ED7,
0xCB2823B2, 0xCEFA28D9, /* 16 */
0xD2F62D3F, 0xD7BB3197,
0xDCE53568, 0xE1FE3875,
0xE7D13B35, 0xED663D35,
0xF39B3EC4, 0xF98E3FA7, /* 24 */
0x00004000, 0x06723FA7,
0x0C653EC4, 0x129A3D35,
0x182F3B35, 0x1E023875,
0x231B3568, 0x28453197, /* 32 */
0x2D0A2D3F, 0x310628D9,
0x34D823B2, 0x38541ED7,
0x3B1318FF, 0x3D1B1369,
0x3EAA0D1E, 0x3F8A0743, /* 40 */
0x3FE300D5, 0x3F8DFA62,
0x3F22F46C, 0x3D96EE2B,
0x3B97E88E, 0x38D7E2B4,
0x35C8DD9F, 0x325AD826, /* 48 */
0x2E03D358, 0x299ACF64,
0x246DCB87,
};
const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE] = {
0x0082, 0x0082, 0x0102, 0x0182, /* 0 */
0x0202, 0x0282, 0x0302, 0x0382,
0x0402, 0x0482, 0x0502, 0x0582,
0x05E2, 0x0662, 0x06E2, 0x0762,
0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */
0x09C2, 0x0A22, 0x0AA2, 0x0B02,
0x0B82, 0x0BE2, 0x0C62, 0x0CC2,
0x0D42, 0x0DA2, 0x0E02, 0x0E62,
0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */
0x1062, 0x10C2, 0x1122, 0x1182,
0x11E2, 0x1242, 0x12A2, 0x12E2,
0x1342, 0x13A2, 0x1402, 0x1442,
0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */
0x15E2, 0x1622, 0x1662, 0x16C1,
0x1701, 0x1741, 0x1781, 0x17E1,
0x1821, 0x1861, 0x18A1, 0x18E1,
0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */
0x1A21, 0x1A61, 0x1AA1, 0x1AC1,
0x1B01, 0x1B41, 0x1B81, 0x1BA1,
0x1BE1, 0x1C21, 0x1C41, 0x1C81,
0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */
0x1D61, 0x1DA1, 0x1DC1, 0x1E01,
0x1E21, 0x1E61, 0x1E81, 0x1EA1,
0x1EE1, 0x1F01, 0x1F21, 0x1F41,
0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */
0x2001, 0x2041, 0x2061, 0x2081,
0x20A1, 0x20C1, 0x20E1, 0x2101,
0x2121, 0x2141, 0x2161, 0x2181,
0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */
0x2221, 0x2241, 0x2261, 0x2281,
0x22A1, 0x22C1, 0x22C1, 0x22E1,
0x2301, 0x2321, 0x2341, 0x2361,
0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */
0x23E1, 0x23E1, 0x2401, 0x2421,
0x2441, 0x2441, 0x2461, 0x2481,
0x2481, 0x24A1, 0x24C1, 0x24C1,
0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */
0x2541, 0x2541, 0x2561, 0x2561,
0x2581, 0x25A1, 0x25A1, 0x25C1,
0x25C1, 0x25E1, 0x2601, 0x2601,
0x2621, 0x2621, 0x2641, 0x2641, /* 160 */
0x2661, 0x2661, 0x2681, 0x2681,
0x26A1, 0x26A1, 0x26C1, 0x26C1,
0x26E1, 0x26E1, 0x2701, 0x2701,
0x2721, 0x2721, 0x2740, 0x2740, /* 176 */
0x2760, 0x2760, 0x2780, 0x2780,
0x2780, 0x27A0, 0x27A0, 0x27C0,
0x27C0, 0x27E0, 0x27E0, 0x27E0,
0x2800, 0x2800, 0x2820, 0x2820, /* 192 */
0x2820, 0x2840, 0x2840, 0x2840,
0x2860, 0x2860, 0x2880, 0x2880,
0x2880, 0x28A0, 0x28A0, 0x28A0,
0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */
0x28E0, 0x28E0, 0x2900, 0x2900,
0x2900, 0x2920, 0x2920, 0x2920,
0x2940, 0x2940, 0x2940, 0x2960,
0x2960, 0x2960, 0x2960, 0x2980, /* 224 */
0x2980, 0x2980, 0x29A0, 0x29A0,
0x29A0, 0x29A0, 0x29C0, 0x29C0,
0x29C0, 0x29E0, 0x29E0, 0x29E0,
0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */
0x2A00, 0x2A20, 0x2A20, 0x2A20,
0x2A20, 0x2A40, 0x2A40, 0x2A40,
0x2A40, 0x2A60, 0x2A60, 0x2A60,
};
const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE] = {
0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */
0x05A9, 0x0669, 0x0709, 0x0789,
0x0829, 0x08A9, 0x0929, 0x0989,
0x0A09, 0x0A69, 0x0AC9, 0x0B29,
0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */
0x0D09, 0x0D69, 0x0DA9, 0x0E09,
0x0E69, 0x0EA9, 0x0F09, 0x0F49,
0x0FA9, 0x0FE9, 0x1029, 0x1089,
0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */
0x11E9, 0x1229, 0x1289, 0x12C9,
0x1309, 0x1349, 0x1389, 0x13C9,
0x1409, 0x1449, 0x14A9, 0x14E9,
0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */
0x1629, 0x1669, 0x16A9, 0x16E8,
0x1728, 0x1768, 0x17A8, 0x17E8,
0x1828, 0x1868, 0x18A8, 0x18E8,
0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */
0x1A28, 0x1A68, 0x1AA8, 0x1AE8,
0x1B28, 0x1B68, 0x1BA8, 0x1BE8,
0x1C28, 0x1C68, 0x1CA8, 0x1CE8,
0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */
0x1E48, 0x1E88, 0x1EC8, 0x1F08,
0x1F48, 0x1F88, 0x1FE8, 0x2028,
0x2068, 0x20A8, 0x2108, 0x2148,
0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */
0x22C8, 0x2308, 0x2348, 0x23A8,
0x23E8, 0x2448, 0x24A8, 0x24E8,
0x2548, 0x25A8, 0x2608, 0x2668,
0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */
0x2847, 0x28C7, 0x2947, 0x29A7,
0x2A27, 0x2AC7, 0x2B47, 0x2BE7,
0x2CA7, 0x2D67, 0x2E47, 0x2F67,
0x3247, 0x3526, 0x3646, 0x3726, /* 128 */
0x3806, 0x38A6, 0x3946, 0x39E6,
0x3A66, 0x3AE6, 0x3B66, 0x3BC6,
0x3C45, 0x3CA5, 0x3D05, 0x3D85,
0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */
0x3F45, 0x3FA5, 0x4005, 0x4045,
0x40A5, 0x40E5, 0x4145, 0x4185,
0x41E5, 0x4225, 0x4265, 0x42C5,
0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */
0x4424, 0x4464, 0x44C4, 0x4504,
0x4544, 0x4584, 0x45C4, 0x4604,
0x4644, 0x46A4, 0x46E4, 0x4724,
0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */
0x4864, 0x48A4, 0x48E4, 0x4924,
0x4964, 0x49A4, 0x49E4, 0x4A24,
0x4A64, 0x4AA4, 0x4AE4, 0x4B23,
0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */
0x4C63, 0x4CA3, 0x4CE3, 0x4D23,
0x4D63, 0x4DA3, 0x4DE3, 0x4E23,
0x4E63, 0x4EA3, 0x4EE3, 0x4F23,
0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */
0x5083, 0x50C3, 0x5103, 0x5143,
0x5183, 0x51E2, 0x5222, 0x5262,
0x52A2, 0x52E2, 0x5342, 0x5382,
0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */
0x5502, 0x5542, 0x55A2, 0x55E2,
0x5642, 0x5682, 0x56E2, 0x5722,
0x5782, 0x57E1, 0x5841, 0x58A1,
0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */
0x5AA1, 0x5B01, 0x5B81, 0x5BE1,
0x5C61, 0x5D01, 0x5D80, 0x5E20,
0x5EE0, 0x5FA0, 0x6080, 0x61C0,
};
const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE] = {
0x0001, 0x0001, 0x0001, 0xFFFE,
0xFFFE, 0x3FFF, 0x1000, 0x0393,
};
const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE] = {
0x4C4C, 0x4C4C, 0x4C4C, 0x2D36,
0x4C4C, 0x4C4C, 0x4C4C, 0x2D36,
};
const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE] = {
0x013C, 0x01F5, 0x031A, 0x0631,
0x0001, 0x0001, 0x0001, 0x0001,
};
const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE] = {
0x5484, 0x3C40, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
};
const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE] = {
0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */
0x2F2D, 0x2A2A, 0x2527, 0x1F21,
0x1A1D, 0x1719, 0x1616, 0x1414,
0x1414, 0x1400, 0x1414, 0x1614,
0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */
0x2A27, 0x2F2A, 0x332D, 0x3B35,
0x5140, 0x6C62, 0x0077,
};
const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE] = {
0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */
0xB2B0, 0xADAD, 0xA7A9, 0x9FA1,
0x969B, 0x9195, 0x8F8F, 0x8A8A,
0x8A8A, 0x8A00, 0x8A8A, 0x8F8A,
0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */
0xADA9, 0xB2AD, 0xB6B0, 0xBCB7,
0xCBC0, 0xD8D4, 0x00DD,
};
const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE] = {
0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */
0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4,
0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4,
0xA4A4, 0xA400, 0xA4A4, 0xA4A4,
0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */
0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4,
0xA4A4, 0xA4A4, 0x00A4,
};
const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE] = {
0x007A, 0x0075, 0x0071, 0x006C, /* 0 */
0x0067, 0x0063, 0x005E, 0x0059,
0x0054, 0x0050, 0x004B, 0x0046,
0x0042, 0x003D, 0x003D, 0x003D,
0x003D, 0x003D, 0x003D, 0x003D, /* 16 */
0x003D, 0x003D, 0x003D, 0x003D,
0x003D, 0x003D, 0x0000, 0x003D,
0x003D, 0x003D, 0x003D, 0x003D,
0x003D, 0x003D, 0x003D, 0x003D, /* 32 */
0x003D, 0x003D, 0x003D, 0x003D,
0x0042, 0x0046, 0x004B, 0x0050,
0x0054, 0x0059, 0x005E, 0x0063,
0x0067, 0x006C, 0x0071, 0x0075, /* 48 */
0x007A,
};
const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE] = {
0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */
0x00D6, 0x00D4, 0x00D2, 0x00CF,
0x00CD, 0x00CA, 0x00C7, 0x00C4,
0x00C1, 0x00BE, 0x00BE, 0x00BE,
0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */
0x00BE, 0x00BE, 0x00BE, 0x00BE,
0x00BE, 0x00BE, 0x0000, 0x00BE,
0x00BE, 0x00BE, 0x00BE, 0x00BE,
0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */
0x00BE, 0x00BE, 0x00BE, 0x00BE,
0x00C1, 0x00C4, 0x00C7, 0x00CA,
0x00CD, 0x00CF, 0x00D2, 0x00D4,
0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */
0x00DE,
};
/**** Helper functions to access the device Internal Lookup Tables ****/
void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val)
{
b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset);
mmiowb();
b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, val);
}
void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, u32 val)
{
b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset);
mmiowb();
b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA2,
(val & 0xFFFF0000) >> 16);
b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1,
val & 0x0000FFFF);
}
u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset)
{
b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset);
return b43legacy_phy_read(dev, B43legacy_PHY_ILT_G_DATA1);
}
#ifndef B43legacy_ILT_H_
#define B43legacy_ILT_H_
#define B43legacy_ILT_ROTOR_SIZE 53
extern const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE];
#define B43legacy_ILT_RETARD_SIZE 53
extern const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE];
#define B43legacy_ILT_FINEFREQA_SIZE 256
extern const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE];
#define B43legacy_ILT_FINEFREQG_SIZE 256
extern const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE];
#define B43legacy_ILT_NOISEA2_SIZE 8
extern const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE];
#define B43legacy_ILT_NOISEA3_SIZE 8
extern const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE];
#define B43legacy_ILT_NOISEG1_SIZE 8
extern const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE];
#define B43legacy_ILT_NOISEG2_SIZE 8
extern const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE];
#define B43legacy_ILT_NOISESCALEG_SIZE 27
extern const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE];
extern const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE];
extern const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE];
#define B43legacy_ILT_SIGMASQR_SIZE 53
extern const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE];
extern const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE];
void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val);
void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset,
u32 val);
u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset);
#endif /* B43legacy_ILT_H_ */
/*
Broadcom B43legacy wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mb@bu3sch.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
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 "leds.h"
#include "b43legacy.h"
#include "main.h"
static void b43legacy_led_changestate(struct b43legacy_led *led)
{
struct b43legacy_wldev *dev = led->dev;
const int index = led->index;
u16 ledctl;
B43legacy_WARN_ON(!(index >= 0 && index < B43legacy_NR_LEDS));
B43legacy_WARN_ON(!led->blink_interval);
ledctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
ledctl ^= (1 << index);
b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ledctl);
}
static void b43legacy_led_blink(unsigned long d)
{
struct b43legacy_led *led = (struct b43legacy_led *)d;
struct b43legacy_wldev *dev = led->dev;
unsigned long flags;
spin_lock_irqsave(&dev->wl->leds_lock, flags);
if (led->blink_interval) {
b43legacy_led_changestate(led);
mod_timer(&led->blink_timer, jiffies + led->blink_interval);
}
spin_unlock_irqrestore(&dev->wl->leds_lock, flags);
}
static void b43legacy_led_blink_start(struct b43legacy_led *led,
unsigned long interval)
{
if (led->blink_interval)
return;
led->blink_interval = interval;
b43legacy_led_changestate(led);
led->blink_timer.expires = jiffies + interval;
add_timer(&led->blink_timer);
}
static void b43legacy_led_blink_stop(struct b43legacy_led *led, int sync)
{
struct b43legacy_wldev *dev = led->dev;
const int index = led->index;
u16 ledctl;
if (!led->blink_interval)
return;
if (unlikely(sync))
del_timer_sync(&led->blink_timer);
else
del_timer(&led->blink_timer);
led->blink_interval = 0;
/* Make sure the LED is turned off. */
B43legacy_WARN_ON(!(index >= 0 && index < B43legacy_NR_LEDS));
ledctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
if (led->activelow)
ledctl |= (1 << index);
else
ledctl &= ~(1 << index);
b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ledctl);
}
static void b43legacy_led_init_hardcoded(struct b43legacy_wldev *dev,
struct b43legacy_led *led,
int led_index)
{
struct ssb_bus *bus = dev->dev->bus;
/* This function is called, if the behaviour (and activelow)
* information for a LED is missing in the SPROM.
* We hardcode the behaviour values for various devices here.
* Note that the B43legacy_LED_TEST_XXX behaviour values can
* be used to figure out which led is mapped to which index.
*/
switch (led_index) {
case 0:
led->behaviour = B43legacy_LED_ACTIVITY;
led->activelow = 1;
if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
led->behaviour = B43legacy_LED_RADIO_ALL;
break;
case 1:
led->behaviour = B43legacy_LED_RADIO_B;
if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
led->behaviour = B43legacy_LED_ASSOC;
break;
case 2:
led->behaviour = B43legacy_LED_RADIO_A;
break;
case 3:
led->behaviour = B43legacy_LED_OFF;
break;
default:
B43legacy_BUG_ON(1);
}
}
int b43legacy_leds_init(struct b43legacy_wldev *dev)
{
struct b43legacy_led *led;
u8 sprom[4];
int i;
sprom[0] = dev->dev->bus->sprom.r1.gpio0;
sprom[1] = dev->dev->bus->sprom.r1.gpio1;
sprom[2] = dev->dev->bus->sprom.r1.gpio2;
sprom[3] = dev->dev->bus->sprom.r1.gpio3;
for (i = 0; i < B43legacy_NR_LEDS; i++) {
led = &(dev->leds[i]);
led->index = i;
led->dev = dev;
setup_timer(&led->blink_timer,
b43legacy_led_blink,
(unsigned long)led);
if (sprom[i] == 0xFF)
b43legacy_led_init_hardcoded(dev, led, i);
else {
led->behaviour = sprom[i] & B43legacy_LED_BEHAVIOUR;
led->activelow = !!(sprom[i] &
B43legacy_LED_ACTIVELOW);
}
}
return 0;
}
void b43legacy_leds_exit(struct b43legacy_wldev *dev)
{
struct b43legacy_led *led;
int i;
for (i = 0; i < B43legacy_NR_LEDS; i++) {
led = &(dev->leds[i]);
b43legacy_led_blink_stop(led, 1);
}
b43legacy_leds_switch_all(dev, 0);
}
void b43legacy_leds_update(struct b43legacy_wldev *dev, int activity)
{
struct b43legacy_led *led;
struct b43legacy_phy *phy = &dev->phy;
const int transferring = (jiffies - dev->stats.last_tx)
< B43legacy_LED_XFER_THRES;
int i;
int turn_on;
unsigned long interval = 0;
u16 ledctl;
unsigned long flags;
spin_lock_irqsave(&dev->wl->leds_lock, flags);
ledctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
for (i = 0; i < B43legacy_NR_LEDS; i++) {
led = &(dev->leds[i]);
turn_on = 0;
switch (led->behaviour) {
case B43legacy_LED_INACTIVE:
continue;
case B43legacy_LED_OFF:
break;
case B43legacy_LED_ON:
turn_on = 1;
break;
case B43legacy_LED_ACTIVITY:
turn_on = activity;
break;
case B43legacy_LED_RADIO_ALL:
turn_on = phy->radio_on &&
b43legacy_is_hw_radio_enabled(dev);
break;
case B43legacy_LED_RADIO_A:
break;
case B43legacy_LED_RADIO_B:
turn_on = (phy->radio_on &&
b43legacy_is_hw_radio_enabled(dev) &&
(phy->type == B43legacy_PHYTYPE_B ||
phy->type == B43legacy_PHYTYPE_G));
break;
case B43legacy_LED_MODE_BG:
if (phy->type == B43legacy_PHYTYPE_G &&
b43legacy_is_hw_radio_enabled(dev))
turn_on = 1;
break;
case B43legacy_LED_TRANSFER:
if (transferring)
b43legacy_led_blink_start(led,
B43legacy_LEDBLINK_MEDIUM);
else
b43legacy_led_blink_stop(led, 0);
continue;
case B43legacy_LED_APTRANSFER:
if (b43legacy_is_mode(dev->wl,
IEEE80211_IF_TYPE_AP)) {
if (transferring) {
interval = B43legacy_LEDBLINK_FAST;
turn_on = 1;
}
} else {
turn_on = 1;
if (transferring)
interval = B43legacy_LEDBLINK_FAST;
else
turn_on = 0;
}
if (turn_on)
b43legacy_led_blink_start(led, interval);
else
b43legacy_led_blink_stop(led, 0);
continue;
case B43legacy_LED_WEIRD:
break;
case B43legacy_LED_ASSOC:
turn_on = 1;
#ifdef CONFIG_B43LEGACY_DEBUG
case B43legacy_LED_TEST_BLINKSLOW:
b43legacy_led_blink_start(led, B43legacy_LEDBLINK_SLOW);
continue;
case B43legacy_LED_TEST_BLINKMEDIUM:
b43legacy_led_blink_start(led,
B43legacy_LEDBLINK_MEDIUM);
continue;
case B43legacy_LED_TEST_BLINKFAST:
b43legacy_led_blink_start(led, B43legacy_LEDBLINK_FAST);
continue;
#endif /* CONFIG_B43LEGACY_DEBUG */
default:
B43legacy_BUG_ON(1);
};
if (led->activelow)
turn_on = !turn_on;
if (turn_on)
ledctl |= (1 << i);
else
ledctl &= ~(1 << i);
}
b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ledctl);
spin_unlock_irqrestore(&dev->wl->leds_lock, flags);
}
void b43legacy_leds_switch_all(struct b43legacy_wldev *dev, int on)
{
struct b43legacy_led *led;
u16 ledctl;
int i;
int bit_on;
unsigned long flags;
spin_lock_irqsave(&dev->wl->leds_lock, flags);
ledctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
for (i = 0; i < B43legacy_NR_LEDS; i++) {
led = &(dev->leds[i]);
if (led->behaviour == B43legacy_LED_INACTIVE)
continue;
if (on)
bit_on = led->activelow ? 0 : 1;
else
bit_on = led->activelow ? 1 : 0;
if (bit_on)
ledctl |= (1 << i);
else
ledctl &= ~(1 << i);
}
b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ledctl);
spin_unlock_irqrestore(&dev->wl->leds_lock, flags);
}
#ifndef B43legacy_LEDS_H_
#define B43legacy_LEDS_H_
#include <linux/types.h>
#include <linux/timer.h>
struct b43legacy_led {
u8 behaviour;
bool activelow;
/* Index in the "leds" array in b43legacy_wldev */
u8 index;
struct b43legacy_wldev *dev;
struct timer_list blink_timer;
unsigned long blink_interval;
};
/* Delay between state changes when blinking in jiffies */
#define B43legacy_LEDBLINK_SLOW (HZ / 1)
#define B43legacy_LEDBLINK_MEDIUM (HZ / 4)
#define B43legacy_LEDBLINK_FAST (HZ / 8)
#define B43legacy_LED_XFER_THRES (HZ / 100)
#define B43legacy_LED_BEHAVIOUR 0x7F
#define B43legacy_LED_ACTIVELOW 0x80
enum { /* LED behaviour values */
B43legacy_LED_OFF,
B43legacy_LED_ON,
B43legacy_LED_ACTIVITY,
B43legacy_LED_RADIO_ALL,
B43legacy_LED_RADIO_A,
B43legacy_LED_RADIO_B,
B43legacy_LED_MODE_BG,
B43legacy_LED_TRANSFER,
B43legacy_LED_APTRANSFER,
B43legacy_LED_WEIRD,
B43legacy_LED_ASSOC,
B43legacy_LED_INACTIVE,
/* Behaviour values for testing.
* With these values it is easier to figure out
* the real behaviour of leds, in case the SPROM
* is missing information.
*/
B43legacy_LED_TEST_BLINKSLOW,
B43legacy_LED_TEST_BLINKMEDIUM,
B43legacy_LED_TEST_BLINKFAST,
};
int b43legacy_leds_init(struct b43legacy_wldev *dev);
void b43legacy_leds_exit(struct b43legacy_wldev *dev);
void b43legacy_leds_update(struct b43legacy_wldev *dev, int activity);
void b43legacy_leds_switch_all(struct b43legacy_wldev *dev, int on);
#endif /* B43legacy_LEDS_H_ */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
Broadcom B43legacy wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Copyright (c) 2005 Stefano Brivio <st3@riseup.net>
Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
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.
*/
#ifndef B43legacy_MAIN_H_
#define B43legacy_MAIN_H_
#include "b43legacy.h"
#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes]
#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes)
/* Magic helper macro to pad structures. Ignore those above. It's magic. */
#define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__ , (nr_bytes))
/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
static inline
u8 b43legacy_freq_to_channel_bg(int freq)
{
u8 channel;
if (freq == 2484)
channel = 14;
else
channel = (freq - 2407) / 5;
return channel;
}
static inline
u8 b43legacy_freq_to_channel(struct b43legacy_wldev *dev,
int freq)
{
return b43legacy_freq_to_channel_bg(freq);
}
/* Lightweight function to convert a channel number to a frequency (in Mhz). */
static inline
int b43legacy_channel_to_freq_bg(u8 channel)
{
int freq;
if (channel == 14)
freq = 2484;
else
freq = 2407 + (5 * channel);
return freq;
}
static inline
int b43legacy_channel_to_freq(struct b43legacy_wldev *dev,
u8 channel)
{
return b43legacy_channel_to_freq_bg(channel);
}
static inline
int b43legacy_is_cck_rate(int rate)
{
return (rate == B43legacy_CCK_RATE_1MB ||
rate == B43legacy_CCK_RATE_2MB ||
rate == B43legacy_CCK_RATE_5MB ||
rate == B43legacy_CCK_RATE_11MB);
}
static inline
int b43legacy_is_ofdm_rate(int rate)
{
return !b43legacy_is_cck_rate(rate);
}
static inline
int b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
{
/* function to return state of hardware enable of radio
* returns 0 if radio disabled, 1 if radio enabled
*/
struct b43legacy_phy *phy = &dev->phy;
if (phy->rev >= 3)
return ((b43legacy_read32(dev,
B43legacy_MMIO_RADIO_HWENABLED_HI)
& B43legacy_MMIO_RADIO_HWENABLED_HI_MASK)
== 0) ? 1 : 0;
else
return ((b43legacy_read16(dev,
B43legacy_MMIO_RADIO_HWENABLED_LO)
& B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
== 0) ? 0 : 1;
}
void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf);
void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf);
u32 b43legacy_shm_read32(struct b43legacy_wldev *dev,
u16 routing, u16 offset);
u16 b43legacy_shm_read16(struct b43legacy_wldev *dev,
u16 routing, u16 offset);
void b43legacy_shm_write32(struct b43legacy_wldev *dev,
u16 routing, u16 offset,
u32 value);
void b43legacy_shm_write16(struct b43legacy_wldev *dev,
u16 routing, u16 offset,
u16 value);
u32 b43legacy_hf_read(struct b43legacy_wldev *dev);
void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value);
void b43legacy_dummy_transmission(struct b43legacy_wldev *dev);
void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags);
void b43legacy_mac_suspend(struct b43legacy_wldev *dev);
void b43legacy_mac_enable(struct b43legacy_wldev *dev);
void b43legacy_controller_restart(struct b43legacy_wldev *dev,
const char *reason);
#endif /* B43legacy_MAIN_H_ */
/*
Broadcom B43legacy 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>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
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 "b43legacy.h"
#include "phy.h"
#include "main.h"
#include "radio.h"
#include "ilt.h"
static const s8 b43legacy_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 b43legacy_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 b43legacy_phy_initg(struct b43legacy_wldev *dev);
static inline
void b43legacy_voluntary_preempt(void)
{
B43legacy_BUG_ON(!(!in_atomic() && !in_irq() &&
!in_interrupt() && !irqs_disabled()));
#ifndef CONFIG_PREEMPT
cond_resched();
#endif /* CONFIG_PREEMPT */
}
void b43legacy_raw_phy_lock(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
B43legacy_WARN_ON(!irqs_disabled());
if (b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD) == 0) {
phy->locked = 0;
return;
}
if (dev->dev->id.revision < 3) {
b43legacy_mac_suspend(dev);
spin_lock(&phy->lock);
} else {
if (!b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_AP))
b43legacy_power_saving_ctl_bits(dev, -1, 1);
}
phy->locked = 1;
}
void b43legacy_raw_phy_unlock(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
B43legacy_WARN_ON(!irqs_disabled());
if (dev->dev->id.revision < 3) {
if (phy->locked) {
spin_unlock(&phy->lock);
b43legacy_mac_enable(dev);
}
} else {
if (!b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_AP))
b43legacy_power_saving_ctl_bits(dev, -1, -1);
}
phy->locked = 0;
}
u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset)
{
b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset);
return b43legacy_read16(dev, B43legacy_MMIO_PHY_DATA);
}
void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val)
{
b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset);
mmiowb();
b43legacy_write16(dev, B43legacy_MMIO_PHY_DATA, val);
}
void b43legacy_phy_calibrate(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD); /* Dummy read. */
if (phy->calibrated)
return;
if (phy->type == B43legacy_PHYTYPE_G && phy->rev == 1) {
b43legacy_wireless_core_reset(dev, 0);
b43legacy_phy_initg(dev);
b43legacy_wireless_core_reset(dev, B43legacy_TMSLOW_GMODE);
}
phy->calibrated = 1;
}
/* intialize B PHY power control
* as described in http://bcm-specs.sipsolutions.net/InitPowerControl
*/
static void b43legacy_phy_init_pctl(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 saved_batt = 0;
u16 saved_ratt = 0;
u16 saved_txctl1 = 0;
int must_reset_txpower = 0;
B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B ||
phy->type == B43legacy_PHYTYPE_G));
if (is_bcm_board_vendor(dev) &&
(dev->dev->bus->boardinfo.type == 0x0416))
return;
b43legacy_phy_write(dev, 0x0028, 0x8018);
b43legacy_write16(dev, 0x03E6, b43legacy_read16(dev, 0x03E6) & 0xFFDF);
if (phy->type == B43legacy_PHYTYPE_G) {
if (!phy->gmode)
return;
b43legacy_phy_write(dev, 0x047A, 0xC111);
}
if (phy->savedpctlreg != 0xFFFF)
return;
#ifdef CONFIG_B43LEGACY_DEBUG
if (phy->manual_txpower_control)
return;
#endif
if (phy->type == B43legacy_PHYTYPE_B &&
phy->rev >= 2 &&
phy->radio_ver == 0x2050)
b43legacy_radio_write16(dev, 0x0076,
b43legacy_radio_read16(dev, 0x0076)
| 0x0084);
else {
saved_batt = phy->bbatt;
saved_ratt = phy->rfatt;
saved_txctl1 = phy->txctl1;
if ((phy->radio_rev >= 6) && (phy->radio_rev <= 8)
&& /*FIXME: incomplete specs for 5 < revision < 9 */ 0)
b43legacy_radio_set_txpower_bg(dev, 0xB, 0x1F, 0);
else
b43legacy_radio_set_txpower_bg(dev, 0xB, 9, 0);
must_reset_txpower = 1;
}
b43legacy_dummy_transmission(dev);
phy->savedpctlreg = b43legacy_phy_read(dev, B43legacy_PHY_G_PCTL);
if (must_reset_txpower)
b43legacy_radio_set_txpower_bg(dev, saved_batt, saved_ratt,
saved_txctl1);
else
b43legacy_radio_write16(dev, 0x0076, b43legacy_radio_read16(dev,
0x0076) & 0xFF7B);
b43legacy_radio_clear_tssi(dev);
}
static void b43legacy_phy_agcsetup(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 offset = 0x0000;
if (phy->rev == 1)
offset = 0x4C00;
b43legacy_ilt_write(dev, offset, 0x00FE);
b43legacy_ilt_write(dev, offset + 1, 0x000D);
b43legacy_ilt_write(dev, offset + 2, 0x0013);
b43legacy_ilt_write(dev, offset + 3, 0x0019);
if (phy->rev == 1) {
b43legacy_ilt_write(dev, 0x1800, 0x2710);
b43legacy_ilt_write(dev, 0x1801, 0x9B83);
b43legacy_ilt_write(dev, 0x1802, 0x9B83);
b43legacy_ilt_write(dev, 0x1803, 0x0F8D);
b43legacy_phy_write(dev, 0x0455, 0x0004);
}
b43legacy_phy_write(dev, 0x04A5, (b43legacy_phy_read(dev, 0x04A5)
& 0x00FF) | 0x5700);
b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A)
& 0xFF80) | 0x000F);
b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A)
& 0xC07F) | 0x2B80);
b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C)
& 0xF0FF) | 0x0300);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x0008);
b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0)
& 0xFFF0) | 0x0008);
b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1)
& 0xF0FF) | 0x0600);
b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2)
& 0xF0FF) | 0x0700);
b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0)
& 0xF0FF) | 0x0100);
if (phy->rev == 1)
b43legacy_phy_write(dev, 0x04A2,
(b43legacy_phy_read(dev, 0x04A2)
& 0xFFF0) | 0x0007);
b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488)
& 0xFF00) | 0x001C);
b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488)
& 0xC0FF) | 0x0200);
b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496)
& 0xFF00) | 0x001C);
b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489)
& 0xFF00) | 0x0020);
b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489)
& 0xC0FF) | 0x0200);
b43legacy_phy_write(dev, 0x0482, (b43legacy_phy_read(dev, 0x0482)
& 0xFF00) | 0x002E);
b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496)
& 0x00FF) | 0x1A00);
b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481)
& 0xFF00) | 0x0028);
b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481)
& 0x00FF) | 0x2C00);
if (phy->rev == 1) {
b43legacy_phy_write(dev, 0x0430, 0x092B);
b43legacy_phy_write(dev, 0x041B,
(b43legacy_phy_read(dev, 0x041B)
& 0xFFE1) | 0x0002);
} else {
b43legacy_phy_write(dev, 0x041B,
b43legacy_phy_read(dev, 0x041B) & 0xFFE1);
b43legacy_phy_write(dev, 0x041F, 0x287A);
b43legacy_phy_write(dev, 0x0420,
(b43legacy_phy_read(dev, 0x0420)
& 0xFFF0) | 0x0004);
}
if (phy->rev > 2) {
b43legacy_phy_write(dev, 0x0422, 0x287A);
b43legacy_phy_write(dev, 0x0420,
(b43legacy_phy_read(dev, 0x0420)
& 0x0FFF) | 0x3000);
}
b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8)
& 0x8080) | 0x7874);
b43legacy_phy_write(dev, 0x048E, 0x1C00);
if (phy->rev == 1) {
b43legacy_phy_write(dev, 0x04AB,
(b43legacy_phy_read(dev, 0x04AB)
& 0xF0FF) | 0x0600);
b43legacy_phy_write(dev, 0x048B, 0x005E);
b43legacy_phy_write(dev, 0x048C,
(b43legacy_phy_read(dev, 0x048C) & 0xFF00)
| 0x001E);
b43legacy_phy_write(dev, 0x048D, 0x0002);
}
b43legacy_ilt_write(dev, offset + 0x0800, 0);
b43legacy_ilt_write(dev, offset + 0x0801, 7);
b43legacy_ilt_write(dev, offset + 0x0802, 16);
b43legacy_ilt_write(dev, offset + 0x0803, 28);
if (phy->rev >= 6) {
b43legacy_phy_write(dev, 0x0426,
(b43legacy_phy_read(dev, 0x0426) & 0xFFFC));
b43legacy_phy_write(dev, 0x0426,
(b43legacy_phy_read(dev, 0x0426) & 0xEFFF));
}
}
static void b43legacy_phy_setupg(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 i;
B43legacy_BUG_ON(phy->type != B43legacy_PHYTYPE_G);
if (phy->rev == 1) {
b43legacy_phy_write(dev, 0x0406, 0x4F19);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
(b43legacy_phy_read(dev,
B43legacy_PHY_G_CRS) & 0xFC3F) | 0x0340);
b43legacy_phy_write(dev, 0x042C, 0x005A);
b43legacy_phy_write(dev, 0x0427, 0x001A);
for (i = 0; i < B43legacy_ILT_FINEFREQG_SIZE; i++)
b43legacy_ilt_write(dev, 0x5800 + i,
b43legacy_ilt_finefreqg[i]);
for (i = 0; i < B43legacy_ILT_NOISEG1_SIZE; i++)
b43legacy_ilt_write(dev, 0x1800 + i,
b43legacy_ilt_noiseg1[i]);
for (i = 0; i < B43legacy_ILT_ROTOR_SIZE; i++)
b43legacy_ilt_write32(dev, 0x2000 + i,
b43legacy_ilt_rotor[i]);
} else {
/* nrssi values are signed 6-bit values. Why 0x7654 here? */
b43legacy_nrssi_hw_write(dev, 0xBA98, (s16)0x7654);
if (phy->rev == 2) {
b43legacy_phy_write(dev, 0x04C0, 0x1861);
b43legacy_phy_write(dev, 0x04C1, 0x0271);
} else if (phy->rev > 2) {
b43legacy_phy_write(dev, 0x04C0, 0x0098);
b43legacy_phy_write(dev, 0x04C1, 0x0070);
b43legacy_phy_write(dev, 0x04C9, 0x0080);
}
b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev,
0x042B) | 0x800);
for (i = 0; i < 64; i++)
b43legacy_ilt_write(dev, 0x4000 + i, i);
for (i = 0; i < B43legacy_ILT_NOISEG2_SIZE; i++)
b43legacy_ilt_write(dev, 0x1800 + i,
b43legacy_ilt_noiseg2[i]);
}
if (phy->rev <= 2)
for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++)
b43legacy_ilt_write(dev, 0x1400 + i,
b43legacy_ilt_noisescaleg1[i]);
else if ((phy->rev >= 7) && (b43legacy_phy_read(dev, 0x0449) & 0x0200))
for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++)
b43legacy_ilt_write(dev, 0x1400 + i,
b43legacy_ilt_noisescaleg3[i]);
else
for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++)
b43legacy_ilt_write(dev, 0x1400 + i,
b43legacy_ilt_noisescaleg2[i]);
if (phy->rev == 2)
for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++)
b43legacy_ilt_write(dev, 0x5000 + i,
b43legacy_ilt_sigmasqr1[i]);
else if ((phy->rev > 2) && (phy->rev <= 8))
for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++)
b43legacy_ilt_write(dev, 0x5000 + i,
b43legacy_ilt_sigmasqr2[i]);
if (phy->rev == 1) {
for (i = 0; i < B43legacy_ILT_RETARD_SIZE; i++)
b43legacy_ilt_write32(dev, 0x2400 + i,
b43legacy_ilt_retard[i]);
for (i = 4; i < 20; i++)
b43legacy_ilt_write(dev, 0x5400 + i, 0x0020);
b43legacy_phy_agcsetup(dev);
if (is_bcm_board_vendor(dev) &&
(dev->dev->bus->boardinfo.type == 0x0416) &&
(dev->dev->bus->boardinfo.rev == 0x0017))
return;
b43legacy_ilt_write(dev, 0x5001, 0x0002);
b43legacy_ilt_write(dev, 0x5002, 0x0001);
} else {
for (i = 0; i <= 0x20; i++)
b43legacy_ilt_write(dev, 0x1000 + i, 0x0820);
b43legacy_phy_agcsetup(dev);
b43legacy_phy_read(dev, 0x0400); /* dummy read */
b43legacy_phy_write(dev, 0x0403, 0x1000);
b43legacy_ilt_write(dev, 0x3C02, 0x000F);
b43legacy_ilt_write(dev, 0x3C03, 0x0014);
if (is_bcm_board_vendor(dev) &&
(dev->dev->bus->boardinfo.type == 0x0416) &&
(dev->dev->bus->boardinfo.rev == 0x0017))
return;
b43legacy_ilt_write(dev, 0x0401, 0x0002);
b43legacy_ilt_write(dev, 0x0402, 0x0001);
}
}
/* Initialize the APHY portion of a GPHY. */
static void b43legacy_phy_inita(struct b43legacy_wldev *dev)
{
might_sleep();
b43legacy_phy_setupg(dev);
if (dev->dev->bus->sprom.r1.boardflags_lo & B43legacy_BFL_PACTRL)
b43legacy_phy_write(dev, 0x046E, 0x03CF);
}
static void b43legacy_phy_initb2(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 offset;
int val;
b43legacy_write16(dev, 0x03EC, 0x3F22);
b43legacy_phy_write(dev, 0x0020, 0x301C);
b43legacy_phy_write(dev, 0x0026, 0x0000);
b43legacy_phy_write(dev, 0x0030, 0x00C6);
b43legacy_phy_write(dev, 0x0088, 0x3E00);
val = 0x3C3D;
for (offset = 0x0089; offset < 0x00A7; offset++) {
b43legacy_phy_write(dev, offset, val);
val -= 0x0202;
}
b43legacy_phy_write(dev, 0x03E4, 0x3000);
if (phy->channel == 0xFF)
b43legacy_radio_selectchannel(dev,
B43legacy_RADIO_DEFAULT_CHANNEL_BG,
0);
else
b43legacy_radio_selectchannel(dev, phy->channel, 0);
if (phy->radio_ver != 0x2050) {
b43legacy_radio_write16(dev, 0x0075, 0x0080);
b43legacy_radio_write16(dev, 0x0079, 0x0081);
}
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x0050, 0x0023);
if (phy->radio_ver == 0x2050) {
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x005A, 0x0070);
b43legacy_radio_write16(dev, 0x005B, 0x007B);
b43legacy_radio_write16(dev, 0x005C, 0x00B0);
b43legacy_radio_write16(dev, 0x007A, 0x000F);
b43legacy_phy_write(dev, 0x0038, 0x0677);
b43legacy_radio_init2050(dev);
}
b43legacy_phy_write(dev, 0x0014, 0x0080);
b43legacy_phy_write(dev, 0x0032, 0x00CA);
b43legacy_phy_write(dev, 0x0032, 0x00CC);
b43legacy_phy_write(dev, 0x0035, 0x07C2);
b43legacy_phy_lo_b_measure(dev);
b43legacy_phy_write(dev, 0x0026, 0xCC00);
if (phy->radio_ver != 0x2050)
b43legacy_phy_write(dev, 0x0026, 0xCE00);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1000);
b43legacy_phy_write(dev, 0x002A, 0x88A3);
if (phy->radio_ver != 0x2050)
b43legacy_phy_write(dev, 0x002A, 0x88C2);
b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
b43legacy_phy_init_pctl(dev);
}
static void b43legacy_phy_initb4(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 offset;
u16 val;
b43legacy_write16(dev, 0x03EC, 0x3F22);
b43legacy_phy_write(dev, 0x0020, 0x301C);
b43legacy_phy_write(dev, 0x0026, 0x0000);
b43legacy_phy_write(dev, 0x0030, 0x00C6);
b43legacy_phy_write(dev, 0x0088, 0x3E00);
val = 0x3C3D;
for (offset = 0x0089; offset < 0x00A7; offset++) {
b43legacy_phy_write(dev, offset, val);
val -= 0x0202;
}
b43legacy_phy_write(dev, 0x03E4, 0x3000);
if (phy->channel == 0xFF)
b43legacy_radio_selectchannel(dev,
B43legacy_RADIO_DEFAULT_CHANNEL_BG,
0);
else
b43legacy_radio_selectchannel(dev, phy->channel, 0);
if (phy->radio_ver != 0x2050) {
b43legacy_radio_write16(dev, 0x0075, 0x0080);
b43legacy_radio_write16(dev, 0x0079, 0x0081);
}
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x0050, 0x0023);
if (phy->radio_ver == 0x2050) {
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x005A, 0x0070);
b43legacy_radio_write16(dev, 0x005B, 0x007B);
b43legacy_radio_write16(dev, 0x005C, 0x00B0);
b43legacy_radio_write16(dev, 0x007A, 0x000F);
b43legacy_phy_write(dev, 0x0038, 0x0677);
b43legacy_radio_init2050(dev);
}
b43legacy_phy_write(dev, 0x0014, 0x0080);
b43legacy_phy_write(dev, 0x0032, 0x00CA);
if (phy->radio_ver == 0x2050)
b43legacy_phy_write(dev, 0x0032, 0x00E0);
b43legacy_phy_write(dev, 0x0035, 0x07C2);
b43legacy_phy_lo_b_measure(dev);
b43legacy_phy_write(dev, 0x0026, 0xCC00);
if (phy->radio_ver == 0x2050)
b43legacy_phy_write(dev, 0x0026, 0xCE00);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1100);
b43legacy_phy_write(dev, 0x002A, 0x88A3);
if (phy->radio_ver == 0x2050)
b43legacy_phy_write(dev, 0x002A, 0x88C2);
b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
if (dev->dev->bus->sprom.r1.boardflags_lo & B43legacy_BFL_RSSI) {
b43legacy_calc_nrssi_slope(dev);
b43legacy_calc_nrssi_threshold(dev);
}
b43legacy_phy_init_pctl(dev);
}
static void b43legacy_phy_initb5(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 offset;
u16 value;
u8 old_channel;
if (phy->analog == 1)
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x0050);
if (!is_bcm_board_vendor(dev) &&
(dev->dev->bus->boardinfo.type != 0x0416)) {
value = 0x2120;
for (offset = 0x00A8 ; offset < 0x00C7; offset++) {
b43legacy_phy_write(dev, offset, value);
value += 0x0202;
}
}
b43legacy_phy_write(dev, 0x0035,
(b43legacy_phy_read(dev, 0x0035) & 0xF0FF)
| 0x0700);
if (phy->radio_ver == 0x2050)
b43legacy_phy_write(dev, 0x0038, 0x0667);
if (phy->gmode) {
if (phy->radio_ver == 0x2050) {
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x0020);
b43legacy_radio_write16(dev, 0x0051,
b43legacy_radio_read16(dev, 0x0051)
| 0x0004);
}
b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, 0x0000);
b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802)
| 0x0100);
b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B)
| 0x2000);
b43legacy_phy_write(dev, 0x001C, 0x186A);
b43legacy_phy_write(dev, 0x0013, (b43legacy_phy_read(dev,
0x0013) & 0x00FF) | 0x1900);
b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev,
0x0035) & 0xFFC0) | 0x0064);
b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
0x005D) & 0xFF80) | 0x000A);
}
if (dev->bad_frames_preempt)
b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
b43legacy_phy_read(dev,
B43legacy_PHY_RADIO_BITFIELD) | (1 << 11));
if (phy->analog == 1) {
b43legacy_phy_write(dev, 0x0026, 0xCE00);
b43legacy_phy_write(dev, 0x0021, 0x3763);
b43legacy_phy_write(dev, 0x0022, 0x1BC3);
b43legacy_phy_write(dev, 0x0023, 0x06F9);
b43legacy_phy_write(dev, 0x0024, 0x037E);
} else
b43legacy_phy_write(dev, 0x0026, 0xCC00);
b43legacy_phy_write(dev, 0x0030, 0x00C6);
b43legacy_write16(dev, 0x03EC, 0x3F22);
if (phy->analog == 1)
b43legacy_phy_write(dev, 0x0020, 0x3E1C);
else
b43legacy_phy_write(dev, 0x0020, 0x301C);
if (phy->analog == 0)
b43legacy_write16(dev, 0x03E4, 0x3000);
old_channel = (phy->channel == 0xFF) ? 1 : phy->channel;
/* Force to channel 7, even if not supported. */
b43legacy_radio_selectchannel(dev, 7, 0);
if (phy->radio_ver != 0x2050) {
b43legacy_radio_write16(dev, 0x0075, 0x0080);
b43legacy_radio_write16(dev, 0x0079, 0x0081);
}
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x0050, 0x0023);
if (phy->radio_ver == 0x2050) {
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x005A, 0x0070);
}
b43legacy_radio_write16(dev, 0x005B, 0x007B);
b43legacy_radio_write16(dev, 0x005C, 0x00B0);
b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev,
0x007A) | 0x0007);
b43legacy_radio_selectchannel(dev, old_channel, 0);
b43legacy_phy_write(dev, 0x0014, 0x0080);
b43legacy_phy_write(dev, 0x0032, 0x00CA);
b43legacy_phy_write(dev, 0x002A, 0x88A3);
b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
if (phy->radio_ver == 0x2050)
b43legacy_radio_write16(dev, 0x005D, 0x000D);
b43legacy_write16(dev, 0x03E4, (b43legacy_read16(dev, 0x03E4) &
0xFFC0) | 0x0004);
}
static void b43legacy_phy_initb6(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 offset;
u16 val;
u8 old_channel;
b43legacy_phy_write(dev, 0x003E, 0x817A);
b43legacy_radio_write16(dev, 0x007A,
(b43legacy_radio_read16(dev, 0x007A) | 0x0058));
if (phy->radio_rev == 4 ||
phy->radio_rev == 5) {
b43legacy_radio_write16(dev, 0x0051, 0x0037);
b43legacy_radio_write16(dev, 0x0052, 0x0070);
b43legacy_radio_write16(dev, 0x0053, 0x00B3);
b43legacy_radio_write16(dev, 0x0054, 0x009B);
b43legacy_radio_write16(dev, 0x005A, 0x0088);
b43legacy_radio_write16(dev, 0x005B, 0x0088);
b43legacy_radio_write16(dev, 0x005D, 0x0088);
b43legacy_radio_write16(dev, 0x005E, 0x0088);
b43legacy_radio_write16(dev, 0x007D, 0x0088);
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
(b43legacy_shm_read32(dev,
B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET)
| 0x00000200));
}
if (phy->radio_rev == 8) {
b43legacy_radio_write16(dev, 0x0051, 0x0000);
b43legacy_radio_write16(dev, 0x0052, 0x0040);
b43legacy_radio_write16(dev, 0x0053, 0x00B7);
b43legacy_radio_write16(dev, 0x0054, 0x0098);
b43legacy_radio_write16(dev, 0x005A, 0x0088);
b43legacy_radio_write16(dev, 0x005B, 0x006B);
b43legacy_radio_write16(dev, 0x005C, 0x000F);
if (dev->dev->bus->sprom.r1.boardflags_lo & 0x8000) {
b43legacy_radio_write16(dev, 0x005D, 0x00FA);
b43legacy_radio_write16(dev, 0x005E, 0x00D8);
} else {
b43legacy_radio_write16(dev, 0x005D, 0x00F5);
b43legacy_radio_write16(dev, 0x005E, 0x00B8);
}
b43legacy_radio_write16(dev, 0x0073, 0x0003);
b43legacy_radio_write16(dev, 0x007D, 0x00A8);
b43legacy_radio_write16(dev, 0x007C, 0x0001);
b43legacy_radio_write16(dev, 0x007E, 0x0008);
}
val = 0x1E1F;
for (offset = 0x0088; offset < 0x0098; offset++) {
b43legacy_phy_write(dev, offset, val);
val -= 0x0202;
}
val = 0x3E3F;
for (offset = 0x0098; offset < 0x00A8; offset++) {
b43legacy_phy_write(dev, offset, val);
val -= 0x0202;
}
val = 0x2120;
for (offset = 0x00A8; offset < 0x00C8; offset++) {
b43legacy_phy_write(dev, offset, (val & 0x3F3F));
val += 0x0202;
}
if (phy->type == B43legacy_PHYTYPE_G) {
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A) |
0x0020);
b43legacy_radio_write16(dev, 0x0051,
b43legacy_radio_read16(dev, 0x0051) |
0x0004);
b43legacy_phy_write(dev, 0x0802,
b43legacy_phy_read(dev, 0x0802) | 0x0100);
b43legacy_phy_write(dev, 0x042B,
b43legacy_phy_read(dev, 0x042B) | 0x2000);
b43legacy_phy_write(dev, 0x5B, 0x0000);
b43legacy_phy_write(dev, 0x5C, 0x0000);
}
old_channel = phy->channel;
if (old_channel >= 8)
b43legacy_radio_selectchannel(dev, 1, 0);
else
b43legacy_radio_selectchannel(dev, 13, 0);
b43legacy_radio_write16(dev, 0x0050, 0x0020);
b43legacy_radio_write16(dev, 0x0050, 0x0023);
udelay(40);
if (phy->radio_rev < 6 || phy->radio_rev == 8) {
b43legacy_radio_write16(dev, 0x007C,
(b43legacy_radio_read16(dev, 0x007C)
| 0x0002));
b43legacy_radio_write16(dev, 0x0050, 0x0020);
}
if (phy->radio_rev <= 2) {
b43legacy_radio_write16(dev, 0x007C, 0x0020);
b43legacy_radio_write16(dev, 0x005A, 0x0070);
b43legacy_radio_write16(dev, 0x005B, 0x007B);
b43legacy_radio_write16(dev, 0x005C, 0x00B0);
}
b43legacy_radio_write16(dev, 0x007A,
(b43legacy_radio_read16(dev,
0x007A) & 0x00F8) | 0x0007);
b43legacy_radio_selectchannel(dev, old_channel, 0);
b43legacy_phy_write(dev, 0x0014, 0x0200);
if (phy->radio_rev >= 6)
b43legacy_phy_write(dev, 0x002A, 0x88C2);
else
b43legacy_phy_write(dev, 0x002A, 0x8AC0);
b43legacy_phy_write(dev, 0x0038, 0x0668);
b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
if (phy->radio_rev <= 5)
b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
0x005D) & 0xFF80) | 0x0003);
if (phy->radio_rev <= 2)
b43legacy_radio_write16(dev, 0x005D, 0x000D);
if (phy->analog == 4) {
b43legacy_write16(dev, 0x03E4, 0x0009);
b43legacy_phy_write(dev, 0x61, b43legacy_phy_read(dev, 0x61)
& 0xFFF);
} else
b43legacy_phy_write(dev, 0x0002, (b43legacy_phy_read(dev,
0x0002) & 0xFFC0) | 0x0004);
if (phy->type == B43legacy_PHYTYPE_G)
b43legacy_write16(dev, 0x03E6, 0x0);
if (phy->type == B43legacy_PHYTYPE_B) {
b43legacy_write16(dev, 0x03E6, 0x8140);
b43legacy_phy_write(dev, 0x0016, 0x0410);
b43legacy_phy_write(dev, 0x0017, 0x0820);
b43legacy_phy_write(dev, 0x0062, 0x0007);
b43legacy_radio_init2050(dev);
b43legacy_phy_lo_g_measure(dev);
if (dev->dev->bus->sprom.r1.boardflags_lo &
B43legacy_BFL_RSSI) {
b43legacy_calc_nrssi_slope(dev);
b43legacy_calc_nrssi_threshold(dev);
}
b43legacy_phy_init_pctl(dev);
}
}
static void b43legacy_calc_loopback_gain(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 backup_phy[15] = {0};
u16 backup_radio[3];
u16 backup_bband;
u16 i;
u16 loop1_cnt;
u16 loop1_done;
u16 loop1_omitted;
u16 loop2_done;
backup_phy[0] = b43legacy_phy_read(dev, 0x0429);
backup_phy[1] = b43legacy_phy_read(dev, 0x0001);
backup_phy[2] = b43legacy_phy_read(dev, 0x0811);
backup_phy[3] = b43legacy_phy_read(dev, 0x0812);
if (phy->rev != 1) {
backup_phy[4] = b43legacy_phy_read(dev, 0x0814);
backup_phy[5] = b43legacy_phy_read(dev, 0x0815);
}
backup_phy[6] = b43legacy_phy_read(dev, 0x005A);
backup_phy[7] = b43legacy_phy_read(dev, 0x0059);
backup_phy[8] = b43legacy_phy_read(dev, 0x0058);
backup_phy[9] = b43legacy_phy_read(dev, 0x000A);
backup_phy[10] = b43legacy_phy_read(dev, 0x0003);
backup_phy[11] = b43legacy_phy_read(dev, 0x080F);
backup_phy[12] = b43legacy_phy_read(dev, 0x0810);
backup_phy[13] = b43legacy_phy_read(dev, 0x002B);
backup_phy[14] = b43legacy_phy_read(dev, 0x0015);
b43legacy_phy_read(dev, 0x002D); /* dummy read */
backup_bband = phy->bbatt;
backup_radio[0] = b43legacy_radio_read16(dev, 0x0052);
backup_radio[1] = b43legacy_radio_read16(dev, 0x0043);
backup_radio[2] = b43legacy_radio_read16(dev, 0x007A);
b43legacy_phy_write(dev, 0x0429,
b43legacy_phy_read(dev, 0x0429) & 0x3FFF);
b43legacy_phy_write(dev, 0x0001,
b43legacy_phy_read(dev, 0x0001) & 0x8000);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811) | 0x0002);
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812) & 0xFFFD);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811) | 0x0001);
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812) & 0xFFFE);
if (phy->rev != 1) {
b43legacy_phy_write(dev, 0x0814,
b43legacy_phy_read(dev, 0x0814) | 0x0001);
b43legacy_phy_write(dev, 0x0815,
b43legacy_phy_read(dev, 0x0815) & 0xFFFE);
b43legacy_phy_write(dev, 0x0814,
b43legacy_phy_read(dev, 0x0814) | 0x0002);
b43legacy_phy_write(dev, 0x0815,
b43legacy_phy_read(dev, 0x0815) & 0xFFFD);
}
b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) |
0x000C);
b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) |
0x000C);
b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811)
& 0xFFCF) | 0x0030);
b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812)
& 0xFFCF) | 0x0010);
b43legacy_phy_write(dev, 0x005A, 0x0780);
b43legacy_phy_write(dev, 0x0059, 0xC810);
b43legacy_phy_write(dev, 0x0058, 0x000D);
if (phy->analog == 0)
b43legacy_phy_write(dev, 0x0003, 0x0122);
else
b43legacy_phy_write(dev, 0x000A,
b43legacy_phy_read(dev, 0x000A)
| 0x2000);
if (phy->rev != 1) {
b43legacy_phy_write(dev, 0x0814,
b43legacy_phy_read(dev, 0x0814) | 0x0004);
b43legacy_phy_write(dev, 0x0815,
b43legacy_phy_read(dev, 0x0815) & 0xFFFB);
}
b43legacy_phy_write(dev, 0x0003,
(b43legacy_phy_read(dev, 0x0003)
& 0xFF9F) | 0x0040);
if (phy->radio_ver == 0x2050 && phy->radio_rev == 2) {
b43legacy_radio_write16(dev, 0x0052, 0x0000);
b43legacy_radio_write16(dev, 0x0043,
(b43legacy_radio_read16(dev, 0x0043)
& 0xFFF0) | 0x0009);
loop1_cnt = 9;
} else if (phy->radio_rev == 8) {
b43legacy_radio_write16(dev, 0x0043, 0x000F);
loop1_cnt = 15;
} else
loop1_cnt = 0;
b43legacy_phy_set_baseband_attenuation(dev, 11);
if (phy->rev >= 3)
b43legacy_phy_write(dev, 0x080F, 0xC020);
else
b43legacy_phy_write(dev, 0x080F, 0x8020);
b43legacy_phy_write(dev, 0x0810, 0x0000);
b43legacy_phy_write(dev, 0x002B,
(b43legacy_phy_read(dev, 0x002B)
& 0xFFC0) | 0x0001);
b43legacy_phy_write(dev, 0x002B,
(b43legacy_phy_read(dev, 0x002B)
& 0xC0FF) | 0x0800);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811) | 0x0100);
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812) & 0xCFFF);
if (dev->dev->bus->sprom.r1.boardflags_lo & B43legacy_BFL_EXTLNA) {
if (phy->rev >= 7) {
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811)
| 0x0800);
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812)
| 0x8000);
}
}
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
& 0x00F7);
for (i = 0; i < loop1_cnt; i++) {
b43legacy_radio_write16(dev, 0x0043, loop1_cnt);
b43legacy_phy_write(dev, 0x0812,
(b43legacy_phy_read(dev, 0x0812)
& 0xF0FF) | (i << 8));
b43legacy_phy_write(dev, 0x0015,
(b43legacy_phy_read(dev, 0x0015)
& 0x0FFF) | 0xA000);
b43legacy_phy_write(dev, 0x0015,
(b43legacy_phy_read(dev, 0x0015)
& 0x0FFF) | 0xF000);
udelay(20);
if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC)
break;
}
loop1_done = i;
loop1_omitted = loop1_cnt - loop1_done;
loop2_done = 0;
if (loop1_done >= 8) {
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812)
| 0x0030);
for (i = loop1_done - 8; i < 16; i++) {
b43legacy_phy_write(dev, 0x0812,
(b43legacy_phy_read(dev, 0x0812)
& 0xF0FF) | (i << 8));
b43legacy_phy_write(dev, 0x0015,
(b43legacy_phy_read(dev, 0x0015)
& 0x0FFF) | 0xA000);
b43legacy_phy_write(dev, 0x0015,
(b43legacy_phy_read(dev, 0x0015)
& 0x0FFF) | 0xF000);
udelay(20);
if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC)
break;
}
}
if (phy->rev != 1) {
b43legacy_phy_write(dev, 0x0814, backup_phy[4]);
b43legacy_phy_write(dev, 0x0815, backup_phy[5]);
}
b43legacy_phy_write(dev, 0x005A, backup_phy[6]);
b43legacy_phy_write(dev, 0x0059, backup_phy[7]);
b43legacy_phy_write(dev, 0x0058, backup_phy[8]);
b43legacy_phy_write(dev, 0x000A, backup_phy[9]);
b43legacy_phy_write(dev, 0x0003, backup_phy[10]);
b43legacy_phy_write(dev, 0x080F, backup_phy[11]);
b43legacy_phy_write(dev, 0x0810, backup_phy[12]);
b43legacy_phy_write(dev, 0x002B, backup_phy[13]);
b43legacy_phy_write(dev, 0x0015, backup_phy[14]);
b43legacy_phy_set_baseband_attenuation(dev, backup_bband);
b43legacy_radio_write16(dev, 0x0052, backup_radio[0]);
b43legacy_radio_write16(dev, 0x0043, backup_radio[1]);
b43legacy_radio_write16(dev, 0x007A, backup_radio[2]);
b43legacy_phy_write(dev, 0x0811, backup_phy[2] | 0x0003);
udelay(10);
b43legacy_phy_write(dev, 0x0811, backup_phy[2]);
b43legacy_phy_write(dev, 0x0812, backup_phy[3]);
b43legacy_phy_write(dev, 0x0429, backup_phy[0]);
b43legacy_phy_write(dev, 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 b43legacy_phy_initg(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 tmp;
if (phy->rev == 1)
b43legacy_phy_initb5(dev);
else
b43legacy_phy_initb6(dev);
if (phy->rev >= 2 || phy->gmode)
b43legacy_phy_inita(dev);
if (phy->rev >= 2) {
b43legacy_phy_write(dev, 0x0814, 0x0000);
b43legacy_phy_write(dev, 0x0815, 0x0000);
}
if (phy->rev == 2) {
b43legacy_phy_write(dev, 0x0811, 0x0000);
b43legacy_phy_write(dev, 0x0015, 0x00C0);
}
if (phy->rev > 5) {
b43legacy_phy_write(dev, 0x0811, 0x0400);
b43legacy_phy_write(dev, 0x0015, 0x00C0);
}
if (phy->rev >= 2 || phy->gmode) {
tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF;
if (tmp == 3 || tmp == 5) {
b43legacy_phy_write(dev, 0x04C2, 0x1816);
b43legacy_phy_write(dev, 0x04C3, 0x8006);
if (tmp == 5)
b43legacy_phy_write(dev, 0x04CC,
(b43legacy_phy_read(dev,
0x04CC) & 0x00FF) |
0x1F00);
}
b43legacy_phy_write(dev, 0x047E, 0x0078);
}
if (phy->radio_rev == 8) {
b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801)
| 0x0080);
b43legacy_phy_write(dev, 0x043E, b43legacy_phy_read(dev, 0x043E)
| 0x0004);
}
if (phy->rev >= 2 && phy->gmode)
b43legacy_calc_loopback_gain(dev);
if (phy->radio_rev != 8) {
if (phy->initval == 0xFFFF)
phy->initval = b43legacy_radio_init2050(dev);
else
b43legacy_radio_write16(dev, 0x0078, phy->initval);
}
if (phy->txctl2 == 0xFFFF)
b43legacy_phy_lo_g_measure(dev);
else {
if (phy->radio_ver == 0x2050 && phy->radio_rev == 8)
b43legacy_radio_write16(dev, 0x0052,
(phy->txctl1 << 4) |
phy->txctl2);
else
b43legacy_radio_write16(dev, 0x0052,
(b43legacy_radio_read16(dev,
0x0052) & 0xFFF0) |
phy->txctl1);
if (phy->rev >= 6)
b43legacy_phy_write(dev, 0x0036,
(b43legacy_phy_read(dev, 0x0036)
& 0x0FFF) | (phy->txctl2 << 12));
if (dev->dev->bus->sprom.r1.boardflags_lo &
B43legacy_BFL_PACTRL)
b43legacy_phy_write(dev, 0x002E, 0x8075);
else
b43legacy_phy_write(dev, 0x002E, 0x807F);
if (phy->rev < 2)
b43legacy_phy_write(dev, 0x002F, 0x0101);
else
b43legacy_phy_write(dev, 0x002F, 0x0202);
}
if (phy->gmode || phy->rev >= 2) {
b43legacy_phy_lo_adjust(dev, 0);
b43legacy_phy_write(dev, 0x080F, 0x8078);
}
if (!(dev->dev->bus->sprom.r1.boardflags_lo & B43legacy_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())
*/
b43legacy_nrssi_hw_update(dev, 0xFFFF);
b43legacy_calc_nrssi_threshold(dev);
} else if (phy->gmode || phy->rev >= 2) {
if (phy->nrssi[0] == -1000) {
B43legacy_WARN_ON(phy->nrssi[1] != -1000);
b43legacy_calc_nrssi_slope(dev);
} else {
B43legacy_WARN_ON(phy->nrssi[1] == -1000);
b43legacy_calc_nrssi_threshold(dev);
}
}
if (phy->radio_rev == 8)
b43legacy_phy_write(dev, 0x0805, 0x3230);
b43legacy_phy_init_pctl(dev);
if (dev->dev->bus->chip_id == 0x4306
&& dev->dev->bus->chip_package == 2) {
b43legacy_phy_write(dev, 0x0429,
b43legacy_phy_read(dev, 0x0429) & 0xBFFF);
b43legacy_phy_write(dev, 0x04C3,
b43legacy_phy_read(dev, 0x04C3) & 0x7FFF);
}
}
static u16 b43legacy_phy_lo_b_r15_loop(struct b43legacy_wldev *dev)
{
int i;
u16 ret = 0;
unsigned long flags;
local_irq_save(flags);
for (i = 0; i < 10; i++) {
b43legacy_phy_write(dev, 0x0015, 0xAFA0);
udelay(1);
b43legacy_phy_write(dev, 0x0015, 0xEFA0);
udelay(10);
b43legacy_phy_write(dev, 0x0015, 0xFFA0);
udelay(40);
ret += b43legacy_phy_read(dev, 0x002C);
}
local_irq_restore(flags);
b43legacy_voluntary_preempt();
return ret;
}
void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 regstack[12] = { 0 };
u16 mls;
u16 fval;
int i;
int j;
regstack[0] = b43legacy_phy_read(dev, 0x0015);
regstack[1] = b43legacy_radio_read16(dev, 0x0052) & 0xFFF0;
if (phy->radio_ver == 0x2053) {
regstack[2] = b43legacy_phy_read(dev, 0x000A);
regstack[3] = b43legacy_phy_read(dev, 0x002A);
regstack[4] = b43legacy_phy_read(dev, 0x0035);
regstack[5] = b43legacy_phy_read(dev, 0x0003);
regstack[6] = b43legacy_phy_read(dev, 0x0001);
regstack[7] = b43legacy_phy_read(dev, 0x0030);
regstack[8] = b43legacy_radio_read16(dev, 0x0043);
regstack[9] = b43legacy_radio_read16(dev, 0x007A);
regstack[10] = b43legacy_read16(dev, 0x03EC);
regstack[11] = b43legacy_radio_read16(dev, 0x0052) & 0x00F0;
b43legacy_phy_write(dev, 0x0030, 0x00FF);
b43legacy_write16(dev, 0x03EC, 0x3F3F);
b43legacy_phy_write(dev, 0x0035, regstack[4] & 0xFF7F);
b43legacy_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0);
}
b43legacy_phy_write(dev, 0x0015, 0xB000);
b43legacy_phy_write(dev, 0x002B, 0x0004);
if (phy->radio_ver == 0x2053) {
b43legacy_phy_write(dev, 0x002B, 0x0203);
b43legacy_phy_write(dev, 0x002A, 0x08A3);
}
phy->minlowsig[0] = 0xFFFF;
for (i = 0; i < 4; i++) {
b43legacy_radio_write16(dev, 0x0052, regstack[1] | i);
b43legacy_phy_lo_b_r15_loop(dev);
}
for (i = 0; i < 10; i++) {
b43legacy_radio_write16(dev, 0x0052, regstack[1] | i);
mls = b43legacy_phy_lo_b_r15_loop(dev) / 10;
if (mls < phy->minlowsig[0]) {
phy->minlowsig[0] = mls;
phy->minlowsigpos[0] = i;
}
}
b43legacy_radio_write16(dev, 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;
b43legacy_phy_write(dev, 0x002F, fval);
mls = b43legacy_phy_lo_b_r15_loop(dev) / 10;
if (mls < phy->minlowsig[1]) {
phy->minlowsig[1] = mls;
phy->minlowsigpos[1] = fval;
}
}
}
phy->minlowsigpos[1] += 0x0101;
b43legacy_phy_write(dev, 0x002F, phy->minlowsigpos[1]);
if (phy->radio_ver == 0x2053) {
b43legacy_phy_write(dev, 0x000A, regstack[2]);
b43legacy_phy_write(dev, 0x002A, regstack[3]);
b43legacy_phy_write(dev, 0x0035, regstack[4]);
b43legacy_phy_write(dev, 0x0003, regstack[5]);
b43legacy_phy_write(dev, 0x0001, regstack[6]);
b43legacy_phy_write(dev, 0x0030, regstack[7]);
b43legacy_radio_write16(dev, 0x0043, regstack[8]);
b43legacy_radio_write16(dev, 0x007A, regstack[9]);
b43legacy_radio_write16(dev, 0x0052,
(b43legacy_radio_read16(dev, 0x0052)
& 0x000F) | regstack[11]);
b43legacy_write16(dev, 0x03EC, regstack[10]);
}
b43legacy_phy_write(dev, 0x0015, regstack[0]);
}
static inline
u16 b43legacy_phy_lo_g_deviation_subval(struct b43legacy_wldev *dev,
u16 control)
{
struct b43legacy_phy *phy = &dev->phy;
u16 ret;
unsigned long flags;
local_irq_save(flags);
if (phy->gmode) {
b43legacy_phy_write(dev, 0x15, 0xE300);
control <<= 8;
b43legacy_phy_write(dev, 0x0812, control | 0x00B0);
udelay(5);
b43legacy_phy_write(dev, 0x0812, control | 0x00B2);
udelay(2);
b43legacy_phy_write(dev, 0x0812, control | 0x00B3);
udelay(4);
b43legacy_phy_write(dev, 0x0015, 0xF300);
udelay(8);
} else {
b43legacy_phy_write(dev, 0x0015, control | 0xEFA0);
udelay(2);
b43legacy_phy_write(dev, 0x0015, control | 0xEFE0);
udelay(4);
b43legacy_phy_write(dev, 0x0015, control | 0xFFE0);
udelay(8);
}
ret = b43legacy_phy_read(dev, 0x002D);
local_irq_restore(flags);
b43legacy_voluntary_preempt();
return ret;
}
static u32 b43legacy_phy_lo_g_singledeviation(struct b43legacy_wldev *dev,
u16 control)
{
int i;
u32 ret = 0;
for (i = 0; i < 8; i++)
ret += b43legacy_phy_lo_g_deviation_subval(dev, control);
return ret;
}
/* Write the LocalOscillator CONTROL */
static inline
void b43legacy_lo_write(struct b43legacy_wldev *dev,
struct b43legacy_lopair *pair)
{
u16 value;
value = (u8)(pair->low);
value |= ((u8)(pair->high)) << 8;
#ifdef CONFIG_B43LEGACY_DEBUG
/* Sanity check. */
if (pair->low < -8 || pair->low > 8 ||
pair->high < -8 || pair->high > 8) {
struct b43legacy_phy *phy = &dev->phy;
b43legacydbg(dev->wl,
"WARNING: Writing invalid LOpair "
"(low: %d, high: %d, index: %lu)\n",
pair->low, pair->high,
(unsigned long)(pair - phy->_lo_pairs));
dump_stack();
}
#endif
b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, value);
}
static inline
struct b43legacy_lopair *b43legacy_find_lopair(struct b43legacy_wldev *dev,
u16 bbatt,
u16 rfatt,
u16 tx)
{
static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 };
struct b43legacy_phy *phy = &dev->phy;
if (bbatt > 6)
bbatt = 6;
B43legacy_WARN_ON(rfatt >= 10);
if (tx == 3)
return b43legacy_get_lopair(phy, rfatt, bbatt);
return b43legacy_get_lopair(phy, dict[rfatt], bbatt);
}
static inline
struct b43legacy_lopair *b43legacy_current_lopair(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
return b43legacy_find_lopair(dev, phy->bbatt,
phy->rfatt, phy->txctl1);
}
/* Adjust B/G LO */
void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed)
{
struct b43legacy_lopair *pair;
if (fixed) {
/* Use fixed values. Only for initialization. */
pair = b43legacy_find_lopair(dev, 2, 3, 0);
} else
pair = b43legacy_current_lopair(dev);
b43legacy_lo_write(dev, pair);
}
static void b43legacy_phy_lo_g_measure_txctl2(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 txctl2 = 0;
u16 i;
u32 smallest;
u32 tmp;
b43legacy_radio_write16(dev, 0x0052, 0x0000);
udelay(10);
smallest = b43legacy_phy_lo_g_singledeviation(dev, 0);
for (i = 0; i < 16; i++) {
b43legacy_radio_write16(dev, 0x0052, i);
udelay(10);
tmp = b43legacy_phy_lo_g_singledeviation(dev, 0);
if (tmp < smallest) {
smallest = tmp;
txctl2 = i;
}
}
phy->txctl2 = txctl2;
}
static
void b43legacy_phy_lo_g_state(struct b43legacy_wldev *dev,
const struct b43legacy_lopair *in_pair,
struct b43legacy_lopair *out_pair,
u16 r27)
{
static const struct b43legacy_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 b43legacy_lopair lowest_transition = {
.high = in_pair->high,
.low = in_pair->low,
};
struct b43legacy_lopair tmp_pair;
struct b43legacy_lopair transition;
int i = 12;
int state = 0;
int found_lower;
int j;
int begin;
int end;
u32 lowest_deviation;
u32 tmp;
/* Note that in_pair and out_pair can point to the same pair.
* Be careful. */
b43legacy_lo_write(dev, &lowest_transition);
lowest_deviation = b43legacy_phy_lo_g_singledeviation(dev, r27);
do {
found_lower = 0;
B43legacy_WARN_ON(!(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) {
B43legacy_WARN_ON(!(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)) {
b43legacy_lo_write(dev, &transition);
tmp = b43legacy_phy_lo_g_singledeviation(dev,
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 b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev,
u16 bbatt)
{
struct b43legacy_phy *phy = &dev->phy;
u16 value;
if (phy->analog == 0) {
value = (b43legacy_read16(dev, 0x03E6) & 0xFFF0);
value |= (bbatt & 0x000F);
b43legacy_write16(dev, 0x03E6, value);
return;
}
if (phy->analog > 1) {
value = b43legacy_phy_read(dev, 0x0060) & 0xFFC3;
value |= (bbatt << 2) & 0x003C;
} else {
value = b43legacy_phy_read(dev, 0x0060) & 0xFF87;
value |= (bbatt << 3) & 0x0078;
}
b43legacy_phy_write(dev, 0x0060, value);
}
/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */
void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev)
{
static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 };
const int is_initializing = (b43legacy_status(dev)
< B43legacy_STAT_STARTED);
struct b43legacy_phy *phy = &dev->phy;
u16 h;
u16 i;
u16 oldi = 0;
u16 j;
struct b43legacy_lopair control;
struct b43legacy_lopair *tmp_control;
u16 tmp;
u16 regstack[16] = { 0 };
u8 oldchannel;
/* XXX: What are these? */
u8 r27 = 0;
u16 r31;
oldchannel = phy->channel;
/* Setup */
if (phy->gmode) {
regstack[0] = b43legacy_phy_read(dev, B43legacy_PHY_G_CRS);
regstack[1] = b43legacy_phy_read(dev, 0x0802);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]
& 0x7FFF);
b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC);
}
regstack[3] = b43legacy_read16(dev, 0x03E2);
b43legacy_write16(dev, 0x03E2, regstack[3] | 0x8000);
regstack[4] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
regstack[5] = b43legacy_phy_read(dev, 0x15);
regstack[6] = b43legacy_phy_read(dev, 0x2A);
regstack[7] = b43legacy_phy_read(dev, 0x35);
regstack[8] = b43legacy_phy_read(dev, 0x60);
regstack[9] = b43legacy_radio_read16(dev, 0x43);
regstack[10] = b43legacy_radio_read16(dev, 0x7A);
regstack[11] = b43legacy_radio_read16(dev, 0x52);
if (phy->gmode) {
regstack[12] = b43legacy_phy_read(dev, 0x0811);
regstack[13] = b43legacy_phy_read(dev, 0x0812);
regstack[14] = b43legacy_phy_read(dev, 0x0814);
regstack[15] = b43legacy_phy_read(dev, 0x0815);
}
b43legacy_radio_selectchannel(dev, 6, 0);
if (phy->gmode) {
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]
& 0x7FFF);
b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC);
b43legacy_dummy_transmission(dev);
}
b43legacy_radio_write16(dev, 0x0043, 0x0006);
b43legacy_phy_set_baseband_attenuation(dev, 2);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x0000);
b43legacy_phy_write(dev, 0x002E, 0x007F);
b43legacy_phy_write(dev, 0x080F, 0x0078);
b43legacy_phy_write(dev, 0x0035, regstack[7] & ~(1 << 7));
b43legacy_radio_write16(dev, 0x007A, regstack[10] & 0xFFF0);
b43legacy_phy_write(dev, 0x002B, 0x0203);
b43legacy_phy_write(dev, 0x002A, 0x08A3);
if (phy->gmode) {
b43legacy_phy_write(dev, 0x0814, regstack[14] | 0x0003);
b43legacy_phy_write(dev, 0x0815, regstack[15] & 0xFFFC);
b43legacy_phy_write(dev, 0x0811, 0x01B3);
b43legacy_phy_write(dev, 0x0812, 0x00B2);
}
if (is_initializing)
b43legacy_phy_lo_g_measure_txctl2(dev);
b43legacy_phy_write(dev, 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 = b43legacy_get_lopair(phy, oldi,
0);
memcpy(&control, tmp_control, sizeof(control));
} else {
tmp_control = b43legacy_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 = b43legacy_get_lopair(phy, i,
j * 2);
if (!tmp_control->used)
continue;
memcpy(&control, tmp_control, sizeof(control));
r27 = 3;
r31 = 0;
}
b43legacy_radio_write16(dev, 0x43, i);
b43legacy_radio_write16(dev, 0x52, phy->txctl2);
udelay(10);
b43legacy_voluntary_preempt();
b43legacy_phy_set_baseband_attenuation(dev, j * 2);
tmp = (regstack[10] & 0xFFF0);
if (r31)
tmp |= 0x0008;
b43legacy_radio_write16(dev, 0x007A, tmp);
tmp_control = b43legacy_get_lopair(phy, i, j * 2);
b43legacy_phy_lo_g_state(dev, &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 = b43legacy_get_lopair(phy, i - 9,
j * 2);
memcpy(&control, tmp_control, sizeof(control));
/* FIXME: The next line is wrong, as the
* following if statement can never trigger. */
tmp = (i - 9) * 2 + j - 5;
r27 = 0;
r31 = 0;
if (tmp > 14) {
r31 = 1;
if (tmp > 17)
r27 = 1;
if (tmp > 19)
r27 = 2;
}
} else {
tmp_control = b43legacy_get_lopair(phy, i - 9,
j * 2);
if (!tmp_control->used)
continue;
memcpy(&control, tmp_control, sizeof(control));
r27 = 3;
r31 = 0;
}
b43legacy_radio_write16(dev, 0x43, i - 9);
/* FIXME: shouldn't txctl1 be zero in the next line
* and 3 in the loop above? */
b43legacy_radio_write16(dev, 0x52,
phy->txctl2
| (3/*txctl1*/ << 4));
udelay(10);
b43legacy_voluntary_preempt();
b43legacy_phy_set_baseband_attenuation(dev, j * 2);
tmp = (regstack[10] & 0xFFF0);
if (r31)
tmp |= 0x0008;
b43legacy_radio_write16(dev, 0x7A, tmp);
tmp_control = b43legacy_get_lopair(phy, i, j * 2);
b43legacy_phy_lo_g_state(dev, &control, tmp_control,
r27);
}
}
/* Restoration */
if (phy->gmode) {
b43legacy_phy_write(dev, 0x0015, 0xE300);
b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA0);
udelay(5);
b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA2);
udelay(2);
b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA3);
b43legacy_voluntary_preempt();
} else
b43legacy_phy_write(dev, 0x0015, r27 | 0xEFA0);
b43legacy_phy_lo_adjust(dev, is_initializing);
b43legacy_phy_write(dev, 0x002E, 0x807F);
if (phy->gmode)
b43legacy_phy_write(dev, 0x002F, 0x0202);
else
b43legacy_phy_write(dev, 0x002F, 0x0101);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, regstack[4]);
b43legacy_phy_write(dev, 0x0015, regstack[5]);
b43legacy_phy_write(dev, 0x002A, regstack[6]);
b43legacy_phy_write(dev, 0x0035, regstack[7]);
b43legacy_phy_write(dev, 0x0060, regstack[8]);
b43legacy_radio_write16(dev, 0x0043, regstack[9]);
b43legacy_radio_write16(dev, 0x007A, regstack[10]);
regstack[11] &= 0x00F0;
regstack[11] |= (b43legacy_radio_read16(dev, 0x52) & 0x000F);
b43legacy_radio_write16(dev, 0x52, regstack[11]);
b43legacy_write16(dev, 0x03E2, regstack[3]);
if (phy->gmode) {
b43legacy_phy_write(dev, 0x0811, regstack[12]);
b43legacy_phy_write(dev, 0x0812, regstack[13]);
b43legacy_phy_write(dev, 0x0814, regstack[14]);
b43legacy_phy_write(dev, 0x0815, regstack[15]);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]);
b43legacy_phy_write(dev, 0x0802, regstack[1]);
}
b43legacy_radio_selectchannel(dev, oldchannel, 1);
#ifdef CONFIG_B43LEGACY_DEBUG
{
/* Sanity check for all lopairs. */
for (i = 0; i < B43legacy_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)
b43legacywarn(dev->wl,
"WARNING: Invalid LOpair (low: %d, high:"
" %d, index: %d)\n",
tmp_control->low, tmp_control->high, i);
}
}
#endif /* CONFIG_B43LEGACY_DEBUG */
}
static
void b43legacy_phy_lo_mark_current_used(struct b43legacy_wldev *dev)
{
struct b43legacy_lopair *pair;
pair = b43legacy_current_lopair(dev);
pair->used = 1;
}
void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
struct b43legacy_lopair *pair;
int i;
for (i = 0; i < B43legacy_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 b43legacy_phy_estimate_power_out(struct b43legacy_wldev *dev, s8 tssi)
{
struct b43legacy_phy *phy = &dev->phy;
s8 dbm = 0;
s32 tmp;
tmp = phy->idle_tssi;
tmp += tssi;
tmp -= phy->savedpctlreg;
switch (phy->type) {
case B43legacy_PHYTYPE_B:
case B43legacy_PHYTYPE_G:
tmp = limit_value(tmp, 0x00, 0x3F);
dbm = phy->tssi2dbm[tmp];
break;
default:
B43legacy_BUG_ON(1);
}
return dbm;
}
/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 tmp;
u16 txpower;
s8 v0;
s8 v1;
s8 v2;
s8 v3;
s8 average;
int max_pwr;
s16 desired_pwr;
s16 estimated_pwr;
s16 pwr_adjust;
s16 radio_att_delta;
s16 baseband_att_delta;
s16 radio_attenuation;
s16 baseband_attenuation;
unsigned long phylock_flags;
if (phy->savedpctlreg == 0xFFFF)
return;
if ((dev->dev->bus->boardinfo.type == 0x0416) &&
is_bcm_board_vendor(dev))
return;
#ifdef CONFIG_B43LEGACY_DEBUG
if (phy->manual_txpower_control)
return;
#endif
B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B ||
phy->type == B43legacy_PHYTYPE_G));
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0058);
v0 = (s8)(tmp & 0x00FF);
v1 = (s8)((tmp & 0xFF00) >> 8);
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005A);
v2 = (s8)(tmp & 0x00FF);
v3 = (s8)((tmp & 0xFF00) >> 8);
tmp = 0;
if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) {
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED,
0x0070);
v0 = (s8)(tmp & 0x00FF);
v1 = (s8)((tmp & 0xFF00) >> 8);
tmp = b43legacy_shm_read16(dev, B43legacy_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;
}
b43legacy_radio_clear_tssi(dev);
average = (v0 + v1 + v2 + v3 + 2) / 4;
if (tmp && (b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005E)
& 0x8))
average -= 13;
estimated_pwr = b43legacy_phy_estimate_power_out(dev, average);
max_pwr = dev->dev->bus->sprom.r1.maxpwr_bg;
if ((dev->dev->bus->sprom.r1.boardflags_lo
& B43legacy_BFL_PACTRL) &&
(phy->type == B43legacy_PHYTYPE_G))
max_pwr -= 0x3;
if (unlikely(max_pwr <= 0)) {
b43legacywarn(dev->wl, "Invalid max-TX-power value in SPROM."
"\n");
max_pwr = 74; /* fake it */
dev->dev->bus->sprom.r1.maxpwr_bg = max_pwr;
}
/* Use regulatory information to get the maximum power.
* In the absence of such data from mac80211, we will use 20 dBm, which
* is the value for the EU, US, Canada, and most of the world.
* The regulatory maximum is reduced by the antenna gain (from sprom)
* and 1.5 dBm (a safety factor??). The result is in Q5.2 format
* which accounts for the factor of 4 */
#define REG_MAX_PWR 20
max_pwr = min(REG_MAX_PWR * 4 - dev->dev->bus->sprom.r1.antenna_gain_bg
- 0x6, max_pwr);
/* find the desired power in Q5.2 - power_level is in dBm
* and limit it - max_pwr is already in Q5.2 */
desired_pwr = limit_value(phy->power_level << 2, 0, max_pwr);
if (b43legacy_debug(dev, B43legacy_DBG_XMITPOWER))
b43legacydbg(dev->wl, "Current TX power output: " Q52_FMT
" dBm, Desired TX power output: " Q52_FMT
" dBm\n", Q52_ARG(estimated_pwr),
Q52_ARG(desired_pwr));
/* Check if we need to adjust the current power. The factor of 2 is
* for damping */
pwr_adjust = (desired_pwr - estimated_pwr) / 2;
/* RF attenuation delta
* The minus sign is because lower attenuation => more power */
radio_att_delta = -(pwr_adjust + 7) >> 3;
/* Baseband attenuation delta */
baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta);
/* Do we need to adjust anything? */
if ((radio_att_delta == 0) && (baseband_att_delta == 0)) {
b43legacy_phy_lo_mark_current_used(dev);
return;
}
/* Calculate the new attenuation values. */
baseband_attenuation = phy->bbatt;
baseband_attenuation += baseband_att_delta;
radio_attenuation = phy->rfatt;
radio_attenuation += radio_att_delta;
/* Get baseband and radio attenuation values into 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 = phy->txctl1;
if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) {
if (radio_attenuation <= 1) {
if (txpower == 0) {
txpower = 3;
radio_attenuation += 2;
baseband_attenuation += 2;
} else if (dev->dev->bus->sprom.r1.boardflags_lo
& B43legacy_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;
}
}
}
/* Save the control values */
phy->txctl1 = txpower;
baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
radio_attenuation = limit_value(radio_attenuation, 0, 9);
phy->rfatt = radio_attenuation;
phy->bbatt = baseband_attenuation;
/* Adjust the hardware */
b43legacy_phy_lock(dev, phylock_flags);
b43legacy_radio_lock(dev);
b43legacy_radio_set_txpower_bg(dev, baseband_attenuation,
radio_attenuation, txpower);
b43legacy_phy_lo_mark_current_used(dev);
b43legacy_radio_unlock(dev);
b43legacy_phy_unlock(dev, phylock_flags);
}
static inline
s32 b43legacy_tssi2dbm_ad(s32 num, s32 den)
{
if (num < 0)
return num/den;
else
return (num+den/2)/den;
}
static inline
s8 b43legacy_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2)
{
s32 m1;
s32 m2;
s32 f = 256;
s32 q;
s32 delta;
s8 i = 0;
m1 = b43legacy_tssi2dbm_ad(16 * pab0 + index * pab1, 32);
m2 = max(b43legacy_tssi2dbm_ad(32768 + index * pab2, 256), 1);
do {
if (i > 15)
return -EINVAL;
q = b43legacy_tssi2dbm_ad(f * 4096 -
b43legacy_tssi2dbm_ad(m2 * f, 16) *
f, 2048);
delta = abs(q - f);
f = q;
i++;
} while (delta >= 2);
entry[index] = limit_value(b43legacy_tssi2dbm_ad(m1 * f, 8192),
-127, 128);
return 0;
}
/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */
int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
s16 pab0;
s16 pab1;
s16 pab2;
u8 idx;
s8 *dyn_tssi2dbm;
B43legacy_WARN_ON(!(phy->type == B43legacy_PHYTYPE_B ||
phy->type == B43legacy_PHYTYPE_G));
pab0 = (s16)(dev->dev->bus->sprom.r1.pa0b0);
pab1 = (s16)(dev->dev->bus->sprom.r1.pa0b1);
pab2 = (s16)(dev->dev->bus->sprom.r1.pa0b2);
if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) {
phy->idle_tssi = 0x34;
phy->tssi2dbm = b43legacy_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 ((s8)dev->dev->bus->sprom.r1.itssi_bg != 0 &&
(s8)dev->dev->bus->sprom.r1.itssi_bg != -1)
phy->idle_tssi = (s8)(dev->dev->bus->sprom.r1.itssi_bg);
else
phy->idle_tssi = 62;
dyn_tssi2dbm = kmalloc(64, GFP_KERNEL);
if (dyn_tssi2dbm == NULL) {
b43legacyerr(dev->wl, "Could not allocate memory"
"for tssi2dbm table\n");
return -ENOMEM;
}
for (idx = 0; idx < 64; idx++)
if (b43legacy_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0,
pab1, pab2)) {
phy->tssi2dbm = NULL;
b43legacyerr(dev->wl, "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 B43legacy_PHYTYPE_B:
phy->idle_tssi = 0x34;
phy->tssi2dbm = b43legacy_tssi2dbm_b_table;
break;
case B43legacy_PHYTYPE_G:
phy->idle_tssi = 0x34;
phy->tssi2dbm = b43legacy_tssi2dbm_g_table;
break;
}
}
return 0;
}
int b43legacy_phy_init(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
int err = -ENODEV;
switch (phy->type) {
case B43legacy_PHYTYPE_B:
switch (phy->rev) {
case 2:
b43legacy_phy_initb2(dev);
err = 0;
break;
case 4:
b43legacy_phy_initb4(dev);
err = 0;
break;
case 5:
b43legacy_phy_initb5(dev);
err = 0;
break;
case 6:
b43legacy_phy_initb6(dev);
err = 0;
break;
}
break;
case B43legacy_PHYTYPE_G:
b43legacy_phy_initg(dev);
err = 0;
break;
}
if (err)
b43legacyerr(dev->wl, "Unknown PHYTYPE found\n");
return err;
}
void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 antennadiv;
u16 offset;
u16 value;
u32 ucodeflags;
antennadiv = phy->antenna_diversity;
if (antennadiv == 0xFFFF)
antennadiv = 3;
B43legacy_WARN_ON(antennadiv > 3);
ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET);
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
ucodeflags & ~B43legacy_UCODEFLAG_AUTODIV);
switch (phy->type) {
case B43legacy_PHYTYPE_G:
offset = 0x0400;
if (antennadiv == 2)
value = (3/*automatic*/ << 7);
else
value = (antennadiv << 7);
b43legacy_phy_write(dev, offset + 1,
(b43legacy_phy_read(dev, offset + 1)
& 0x7E7F) | value);
if (antennadiv >= 2) {
if (antennadiv == 2)
value = (antennadiv << 7);
else
value = (0/*force0*/ << 7);
b43legacy_phy_write(dev, offset + 0x2B,
(b43legacy_phy_read(dev,
offset + 0x2B)
& 0xFEFF) | value);
}
if (phy->type == B43legacy_PHYTYPE_G) {
if (antennadiv >= 2)
b43legacy_phy_write(dev, 0x048C,
b43legacy_phy_read(dev,
0x048C) | 0x2000);
else
b43legacy_phy_write(dev, 0x048C,
b43legacy_phy_read(dev,
0x048C) & ~0x2000);
if (phy->rev >= 2) {
b43legacy_phy_write(dev, 0x0461,
b43legacy_phy_read(dev,
0x0461) | 0x0010);
b43legacy_phy_write(dev, 0x04AD,
(b43legacy_phy_read(dev,
0x04AD)
& 0x00FF) | 0x0015);
if (phy->rev == 2)
b43legacy_phy_write(dev, 0x0427,
0x0008);
else
b43legacy_phy_write(dev, 0x0427,
(b43legacy_phy_read(dev, 0x0427)
& 0x00FF) | 0x0008);
} else if (phy->rev >= 6)
b43legacy_phy_write(dev, 0x049B, 0x00DC);
} else {
if (phy->rev < 3)
b43legacy_phy_write(dev, 0x002B,
(b43legacy_phy_read(dev,
0x002B) & 0x00FF)
| 0x0024);
else {
b43legacy_phy_write(dev, 0x0061,
b43legacy_phy_read(dev,
0x0061) | 0x0010);
if (phy->rev == 3) {
b43legacy_phy_write(dev, 0x0093,
0x001D);
b43legacy_phy_write(dev, 0x0027,
0x0008);
} else {
b43legacy_phy_write(dev, 0x0093,
0x003A);
b43legacy_phy_write(dev, 0x0027,
(b43legacy_phy_read(dev, 0x0027)
& 0x00FF) | 0x0008);
}
}
}
break;
case B43legacy_PHYTYPE_B:
if (dev->dev->id.revision == 2)
value = (3/*automatic*/ << 7);
else
value = (antennadiv << 7);
b43legacy_phy_write(dev, 0x03E2,
(b43legacy_phy_read(dev, 0x03E2)
& 0xFE7F) | value);
break;
default:
B43legacy_WARN_ON(1);
}
if (antennadiv >= 2) {
ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET);
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
ucodeflags | B43legacy_UCODEFLAG_AUTODIV);
}
phy->antenna_diversity = antennadiv;
}
/* Set the PowerSavingControlBits.
* Bitvalues:
* 0 => unset the bit
* 1 => set the bit
* -1 => calculate the bit
*/
void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev,
int bit25, int bit26)
{
int i;
u32 status;
/* FIXME: Force 25 to off and 26 to on for now: */
bit25 = 0;
bit26 = 1;
if (bit25 == -1) {
/* TODO: If powersave is not off and FIXME is not set and we
* are not in adhoc and thus is not an AP and we arei
* associated, set bit 25 */
}
if (bit26 == -1) {
/* TODO: If the device is awake or this is an AP, or we are
* scanning, or FIXME, or we are associated, or FIXME,
* or the latest PS-Poll packet sent was successful,
* set bit26 */
}
status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
if (bit25)
status |= B43legacy_SBF_PS1;
else
status &= ~B43legacy_SBF_PS1;
if (bit26)
status |= B43legacy_SBF_PS2;
else
status &= ~B43legacy_SBF_PS2;
b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
if (bit26 && dev->dev->id.revision >= 5) {
for (i = 0; i < 100; i++) {
if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
0x0040) != 4)
break;
udelay(10);
}
}
}
/*
Broadcom B43legacy 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>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
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.
*/
#ifndef B43legacy_PHY_H_
#define B43legacy_PHY_H_
#include <linux/types.h>
enum {
B43legacy_ANTENNA0, /* Antenna 0 */
B43legacy_ANTENNA1, /* Antenna 0 */
B43legacy_ANTENNA_AUTO1, /* Automatic, starting with antenna 1 */
B43legacy_ANTENNA_AUTO0, /* Automatic, starting with antenna 0 */
B43legacy_ANTENNA_AUTO = B43legacy_ANTENNA_AUTO0,
B43legacy_ANTENNA_DEFAULT = B43legacy_ANTENNA_AUTO,
};
enum {
B43legacy_INTERFMODE_NONE,
B43legacy_INTERFMODE_NONWLAN,
B43legacy_INTERFMODE_MANUALWLAN,
B43legacy_INTERFMODE_AUTOWLAN,
};
/*** PHY Registers ***/
/* Routing */
#define B43legacy_PHYROUTE_OFDM_GPHY 0x400
#define B43legacy_PHYROUTE_EXT_GPHY 0x800
/* Base registers. */
#define B43legacy_PHY_BASE(reg) (reg)
/* OFDM (A) registers of a G-PHY */
#define B43legacy_PHY_OFDM(reg) ((reg) | B43legacy_PHYROUTE_OFDM_GPHY)
/* Extended G-PHY registers */
#define B43legacy_PHY_EXTG(reg) ((reg) | B43legacy_PHYROUTE_EXT_GPHY)
/* Extended G-PHY Registers */
#define B43legacy_PHY_CLASSCTL B43legacy_PHY_EXTG(0x02) /* Classify control */
#define B43legacy_PHY_GTABCTL B43legacy_PHY_EXTG(0x03) /* G-PHY table control (see below) */
#define B43legacy_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */
#define B43legacy_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */
#define B43legacy_PHY_GTABNR_SHIFT 10
#define B43legacy_PHY_GTABDATA B43legacy_PHY_EXTG(0x04) /* G-PHY table data */
#define B43legacy_PHY_LO_MASK B43legacy_PHY_EXTG(0x0F) /* Local Oscillator control mask */
#define B43legacy_PHY_LO_CTL B43legacy_PHY_EXTG(0x10) /* Local Oscillator control */
#define B43legacy_PHY_RFOVER B43legacy_PHY_EXTG(0x11) /* RF override */
#define B43legacy_PHY_RFOVERVAL B43legacy_PHY_EXTG(0x12) /* RF override value */
/*** OFDM table numbers ***/
#define B43legacy_OFDMTAB(number, offset) \
(((number) << B43legacy_PHY_OTABLENR_SHIFT) \
| (offset))
#define B43legacy_OFDMTAB_AGC1 B43legacy_OFDMTAB(0x00, 0)
#define B43legacy_OFDMTAB_GAIN0 B43legacy_OFDMTAB(0x00, 0)
#define B43legacy_OFDMTAB_GAINX B43legacy_OFDMTAB(0x01, 0)
#define B43legacy_OFDMTAB_GAIN1 B43legacy_OFDMTAB(0x01, 4)
#define B43legacy_OFDMTAB_AGC3 B43legacy_OFDMTAB(0x02, 0)
#define B43legacy_OFDMTAB_GAIN2 B43legacy_OFDMTAB(0x02, 3)
#define B43legacy_OFDMTAB_LNAHPFGAIN1 B43legacy_OFDMTAB(0x03, 0)
#define B43legacy_OFDMTAB_WRSSI B43legacy_OFDMTAB(0x04, 0)
#define B43legacy_OFDMTAB_LNAHPFGAIN2 B43legacy_OFDMTAB(0x04, 0)
#define B43legacy_OFDMTAB_NOISESCALE B43legacy_OFDMTAB(0x05, 0)
#define B43legacy_OFDMTAB_AGC2 B43legacy_OFDMTAB(0x06, 0)
#define B43legacy_OFDMTAB_ROTOR B43legacy_OFDMTAB(0x08, 0)
#define B43legacy_OFDMTAB_ADVRETARD B43legacy_OFDMTAB(0x09, 0)
#define B43legacy_OFDMTAB_DAC B43legacy_OFDMTAB(0x0C, 0)
#define B43legacy_OFDMTAB_DC B43legacy_OFDMTAB(0x0E, 7)
#define B43legacy_OFDMTAB_PWRDYN2 B43legacy_OFDMTAB(0x0E, 12)
#define B43legacy_OFDMTAB_LNAGAIN B43legacy_OFDMTAB(0x0E, 13)
#define B43legacy_OFDMTAB_LPFGAIN B43legacy_OFDMTAB(0x0F, 12)
#define B43legacy_OFDMTAB_RSSI B43legacy_OFDMTAB(0x10, 0)
#define B43legacy_OFDMTAB_AGC1_R1 B43legacy_OFDMTAB(0x13, 0)
#define B43legacy_OFDMTAB_GAINX_R1 B43legacy_OFDMTAB(0x14, 0)
#define B43legacy_OFDMTAB_MINSIGSQ B43legacy_OFDMTAB(0x14, 1)
#define B43legacy_OFDMTAB_AGC3_R1 B43legacy_OFDMTAB(0x15, 0)
#define B43legacy_OFDMTAB_WRSSI_R1 B43legacy_OFDMTAB(0x15, 4)
#define B43legacy_OFDMTAB_TSSI B43legacy_OFDMTAB(0x15, 0)
#define B43legacy_OFDMTAB_DACRFPABB B43legacy_OFDMTAB(0x16, 0)
#define B43legacy_OFDMTAB_DACOFF B43legacy_OFDMTAB(0x17, 0)
#define B43legacy_OFDMTAB_DCBIAS B43legacy_OFDMTAB(0x18, 0)
void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt);
/* OFDM (A) PHY Registers */
#define B43legacy_PHY_VERSION_OFDM B43legacy_PHY_OFDM(0x00) /* Versioning register for A-PHY */
#define B43legacy_PHY_BBANDCFG B43legacy_PHY_OFDM(0x01) /* Baseband config */
#define B43legacy_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */
#define B43legacy_PHY_BBANDCFG_RXANT_SHIFT 7
#define B43legacy_PHY_PWRDOWN B43legacy_PHY_OFDM(0x03) /* Powerdown */
#define B43legacy_PHY_CRSTHRES1 B43legacy_PHY_OFDM(0x06) /* CRS Threshold 1 */
#define B43legacy_PHY_LNAHPFCTL B43legacy_PHY_OFDM(0x1C) /* LNA/HPF control */
#define B43legacy_PHY_ADIVRELATED B43legacy_PHY_OFDM(0x27) /* FIXME rename */
#define B43legacy_PHY_CRS0 B43legacy_PHY_OFDM(0x29)
#define B43legacy_PHY_ANTDWELL B43legacy_PHY_OFDM(0x2B) /* Antenna dwell */
#define B43legacy_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */
#define B43legacy_PHY_ENCORE B43legacy_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */
#define B43legacy_PHY_ENCORE_EN 0x0200 /* Encore enable */
#define B43legacy_PHY_LMS B43legacy_PHY_OFDM(0x55)
#define B43legacy_PHY_OFDM61 B43legacy_PHY_OFDM(0x61) /* FIXME rename */
#define B43legacy_PHY_OFDM61_10 0x0010 /* FIXME rename */
#define B43legacy_PHY_IQBAL B43legacy_PHY_OFDM(0x69) /* I/Q balance */
#define B43legacy_PHY_OTABLECTL B43legacy_PHY_OFDM(0x72) /* OFDM table control (see below) */
#define B43legacy_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */
#define B43legacy_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */
#define B43legacy_PHY_OTABLENR_SHIFT 10
#define B43legacy_PHY_OTABLEI B43legacy_PHY_OFDM(0x73) /* OFDM table data I */
#define B43legacy_PHY_OTABLEQ B43legacy_PHY_OFDM(0x74) /* OFDM table data Q */
#define B43legacy_PHY_HPWR_TSSICTL B43legacy_PHY_OFDM(0x78) /* Hardware power TSSI control */
#define B43legacy_PHY_NRSSITHRES B43legacy_PHY_OFDM(0x8A) /* NRSSI threshold */
#define B43legacy_PHY_ANTWRSETT B43legacy_PHY_OFDM(0x8C) /* Antenna WR settle */
#define B43legacy_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */
#define B43legacy_PHY_CLIPPWRDOWNT B43legacy_PHY_OFDM(0x93) /* Clip powerdown threshold */
#define B43legacy_PHY_OFDM9B B43legacy_PHY_OFDM(0x9B) /* FIXME rename */
#define B43legacy_PHY_N1P1GAIN B43legacy_PHY_OFDM(0xA0)
#define B43legacy_PHY_P1P2GAIN B43legacy_PHY_OFDM(0xA1)
#define B43legacy_PHY_N1N2GAIN B43legacy_PHY_OFDM(0xA2)
#define B43legacy_PHY_CLIPTHRES B43legacy_PHY_OFDM(0xA3)
#define B43legacy_PHY_CLIPN1P2THRES B43legacy_PHY_OFDM(0xA4)
#define B43legacy_PHY_DIVSRCHIDX B43legacy_PHY_OFDM(0xA8) /* Divider search gain/index */
#define B43legacy_PHY_CLIPP2THRES B43legacy_PHY_OFDM(0xA9)
#define B43legacy_PHY_CLIPP3THRES B43legacy_PHY_OFDM(0xAA)
#define B43legacy_PHY_DIVP1P2GAIN B43legacy_PHY_OFDM(0xAB)
#define B43legacy_PHY_DIVSRCHGAINBACK B43legacy_PHY_OFDM(0xAD) /* Divider search gain back */
#define B43legacy_PHY_DIVSRCHGAINCHNG B43legacy_PHY_OFDM(0xAE) /* Divider search gain change */
#define B43legacy_PHY_CRSTHRES1_R1 B43legacy_PHY_OFDM(0xC0) /* CRS Threshold 1 (rev 1 only) */
#define B43legacy_PHY_CRSTHRES2_R1 B43legacy_PHY_OFDM(0xC1) /* CRS Threshold 2 (rev 1 only) */
#define B43legacy_PHY_TSSIP_LTBASE B43legacy_PHY_OFDM(0x380) /* TSSI power lookup table base */
#define B43legacy_PHY_DC_LTBASE B43legacy_PHY_OFDM(0x3A0) /* DC lookup table base */
#define B43legacy_PHY_GAIN_LTBASE B43legacy_PHY_OFDM(0x3C0) /* Gain lookup table base */
void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt);
/* Masks for the different PHY versioning registers. */
#define B43legacy_PHYVER_ANALOG 0xF000
#define B43legacy_PHYVER_ANALOG_SHIFT 12
#define B43legacy_PHYVER_TYPE 0x0F00
#define B43legacy_PHYVER_TYPE_SHIFT 8
#define B43legacy_PHYVER_VERSION 0x00FF
struct b43legacy_wldev;
void b43legacy_raw_phy_lock(struct b43legacy_wldev *dev);
#define b43legacy_phy_lock(bcm, flags) \
do { \
local_irq_save(flags); \
b43legacy_raw_phy_lock(bcm); \
} while (0)
void b43legacy_raw_phy_unlock(struct b43legacy_wldev *dev);
#define b43legacy_phy_unlock(bcm, flags) \
do { \
b43legacy_raw_phy_unlock(bcm); \
local_irq_restore(flags); \
} while (0)
/* Card uses the loopback gain stuff */
#define has_loopback_gain(phy) \
(((phy)->rev > 1) || ((phy)->gmode))
u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset);
void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val);
int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev);
int b43legacy_phy_init(struct b43legacy_wldev *dev);
void b43legacy_set_rx_antenna(struct b43legacy_wldev *dev, int antenna);
void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev);
void b43legacy_phy_calibrate(struct b43legacy_wldev *dev);
int b43legacy_phy_connect(struct b43legacy_wldev *dev, int connect);
void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev);
void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev);
void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev);
/* Adjust the LocalOscillator to the saved values.
* "fixed" is only set to 1 once in initialization. Set to 0 otherwise.
*/
void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed);
void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev);
void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev,
u16 baseband_attenuation);
void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev,
int bit25, int bit26);
#endif /* B43legacy_PHY_H_ */
/*
Broadcom B43legacy wireless driver
PIO Transmission
Copyright (c) 2005 Michael Buesch <mb@bu3sch.de>
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 "b43legacy.h"
#include "pio.h"
#include "main.h"
#include "xmit.h"
#include <linux/delay.h>
static void tx_start(struct b43legacy_pioqueue *queue)
{
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
B43legacy_PIO_TXCTL_INIT);
}
static void tx_octet(struct b43legacy_pioqueue *queue,
u8 octet)
{
if (queue->need_workarounds) {
b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet);
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
B43legacy_PIO_TXCTL_WRITELO);
} else {
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
B43legacy_PIO_TXCTL_WRITELO);
b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet);
}
}
static u16 tx_get_next_word(const u8 *txhdr,
const u8 *packet,
size_t txhdr_size,
unsigned int *pos)
{
const u8 *source;
unsigned int i = *pos;
u16 ret;
if (i < txhdr_size)
source = txhdr;
else {
source = packet;
i -= txhdr_size;
}
ret = le16_to_cpu(*((__le16 *)(source + i)));
*pos += 2;
return ret;
}
static void tx_data(struct b43legacy_pioqueue *queue,
u8 *txhdr,
const u8 *packet,
unsigned int octets)
{
u16 data;
unsigned int i = 0;
if (queue->need_workarounds) {
data = tx_get_next_word(txhdr, packet,
sizeof(struct b43legacy_txhdr_fw3), &i);
b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data);
}
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
B43legacy_PIO_TXCTL_WRITELO |
B43legacy_PIO_TXCTL_WRITEHI);
while (i < octets - 1) {
data = tx_get_next_word(txhdr, packet,
sizeof(struct b43legacy_txhdr_fw3), &i);
b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data);
}
if (octets % 2)
tx_octet(queue, packet[octets -
sizeof(struct b43legacy_txhdr_fw3) - 1]);
}
static void tx_complete(struct b43legacy_pioqueue *queue,
struct sk_buff *skb)
{
if (queue->need_workarounds) {
b43legacy_pio_write(queue, B43legacy_PIO_TXDATA,
skb->data[skb->len - 1]);
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
B43legacy_PIO_TXCTL_WRITELO |
B43legacy_PIO_TXCTL_COMPLETE);
} else
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
B43legacy_PIO_TXCTL_COMPLETE);
}
static u16 generate_cookie(struct b43legacy_pioqueue *queue,
struct b43legacy_pio_txpacket *packet)
{
u16 cookie = 0x0000;
int packetindex;
/* We use the upper 4 bits for the PIO
* controller ID and the lower 12 bits
* for the packet index (in the cache).
*/
switch (queue->mmio_base) {
case B43legacy_MMIO_PIO1_BASE:
break;
case B43legacy_MMIO_PIO2_BASE:
cookie = 0x1000;
break;
case B43legacy_MMIO_PIO3_BASE:
cookie = 0x2000;
break;
case B43legacy_MMIO_PIO4_BASE:
cookie = 0x3000;
break;
default:
B43legacy_WARN_ON(1);
}
packetindex = pio_txpacket_getindex(packet);
B43legacy_WARN_ON(!(((u16)packetindex & 0xF000) == 0x0000));
cookie |= (u16)packetindex;
return cookie;
}
static
struct b43legacy_pioqueue *parse_cookie(struct b43legacy_wldev *dev,
u16 cookie,
struct b43legacy_pio_txpacket **packet)
{
struct b43legacy_pio *pio = &dev->pio;
struct b43legacy_pioqueue *queue = NULL;
int packetindex;
switch (cookie & 0xF000) {
case 0x0000:
queue = pio->queue0;
break;
case 0x1000:
queue = pio->queue1;
break;
case 0x2000:
queue = pio->queue2;
break;
case 0x3000:
queue = pio->queue3;
break;
default:
B43legacy_WARN_ON(1);
}
packetindex = (cookie & 0x0FFF);
B43legacy_WARN_ON(!(packetindex >= 0 && packetindex
< B43legacy_PIO_MAXTXPACKETS));
*packet = &(queue->tx_packets_cache[packetindex]);
return queue;
}
union txhdr_union {
struct b43legacy_txhdr_fw3 txhdr_fw3;
};
static void pio_tx_write_fragment(struct b43legacy_pioqueue *queue,
struct sk_buff *skb,
struct b43legacy_pio_txpacket *packet,
size_t txhdr_size)
{
union txhdr_union txhdr_data;
u8 *txhdr = NULL;
unsigned int octets;
txhdr = (u8 *)(&txhdr_data.txhdr_fw3);
B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0);
b43legacy_generate_txhdr(queue->dev,
txhdr, skb->data, skb->len,
&packet->txstat.control,
generate_cookie(queue, packet));
tx_start(queue);
octets = skb->len + txhdr_size;
if (queue->need_workarounds)
octets--;
tx_data(queue, txhdr, (u8 *)skb->data, octets);
tx_complete(queue, skb);
}
static void free_txpacket(struct b43legacy_pio_txpacket *packet,
int irq_context)
{
struct b43legacy_pioqueue *queue = packet->queue;
if (packet->skb) {
if (irq_context)
dev_kfree_skb_irq(packet->skb);
else
dev_kfree_skb(packet->skb);
}
list_move(&packet->list, &queue->txfree);
queue->nr_txfree++;
}
static int pio_tx_packet(struct b43legacy_pio_txpacket *packet)
{
struct b43legacy_pioqueue *queue = packet->queue;
struct sk_buff *skb = packet->skb;
u16 octets;
octets = (u16)skb->len + sizeof(struct b43legacy_txhdr_fw3);
if (queue->tx_devq_size < octets) {
b43legacywarn(queue->dev->wl, "PIO queue too small. "
"Dropping packet.\n");
/* Drop it silently (return success) */
free_txpacket(packet, 1);
return 0;
}
B43legacy_WARN_ON(queue->tx_devq_packets >
B43legacy_PIO_MAXTXDEVQPACKETS);
B43legacy_WARN_ON(queue->tx_devq_used > queue->tx_devq_size);
/* Check if there is sufficient free space on the device
* TX queue. If not, return and let the TX tasklet
* retry later.
*/
if (queue->tx_devq_packets == B43legacy_PIO_MAXTXDEVQPACKETS)
return -EBUSY;
if (queue->tx_devq_used + octets > queue->tx_devq_size)
return -EBUSY;
/* Now poke the device. */
pio_tx_write_fragment(queue, skb, packet,
sizeof(struct b43legacy_txhdr_fw3));
/* Account for the packet size.
* (We must not overflow the device TX queue)
*/
queue->tx_devq_packets++;
queue->tx_devq_used += octets;
/* Transmission started, everything ok, move the
* packet to the txrunning list.
*/
list_move_tail(&packet->list, &queue->txrunning);
return 0;
}
static void tx_tasklet(unsigned long d)
{
struct b43legacy_pioqueue *queue = (struct b43legacy_pioqueue *)d;
struct b43legacy_wldev *dev = queue->dev;
unsigned long flags;
struct b43legacy_pio_txpacket *packet, *tmp_packet;
int err;
u16 txctl;
spin_lock_irqsave(&dev->wl->irq_lock, flags);
if (queue->tx_frozen)
goto out_unlock;
txctl = b43legacy_pio_read(queue, B43legacy_PIO_TXCTL);
if (txctl & B43legacy_PIO_TXCTL_SUSPEND)
goto out_unlock;
list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) {
/* Try to transmit the packet. This can fail, if
* the device queue is full. In case of failure, the
* packet is left in the txqueue.
* If transmission succeed, the packet is moved to txrunning.
* If it is impossible to transmit the packet, it
* is dropped.
*/
err = pio_tx_packet(packet);
if (err)
break;
}
out_unlock:
spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
}
static void setup_txqueues(struct b43legacy_pioqueue *queue)
{
struct b43legacy_pio_txpacket *packet;
int i;
queue->nr_txfree = B43legacy_PIO_MAXTXPACKETS;
for (i = 0; i < B43legacy_PIO_MAXTXPACKETS; i++) {
packet = &(queue->tx_packets_cache[i]);
packet->queue = queue;
INIT_LIST_HEAD(&packet->list);
list_add(&packet->list, &queue->txfree);
}
}
static
struct b43legacy_pioqueue *b43legacy_setup_pioqueue(struct b43legacy_wldev *dev,
u16 pio_mmio_base)
{
struct b43legacy_pioqueue *queue;
u32 value;
u16 qsize;
queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue)
goto out;
queue->dev = dev;
queue->mmio_base = pio_mmio_base;
queue->need_workarounds = (dev->dev->id.revision < 3);
INIT_LIST_HEAD(&queue->txfree);
INIT_LIST_HEAD(&queue->txqueue);
INIT_LIST_HEAD(&queue->txrunning);
tasklet_init(&queue->txtask, tx_tasklet,
(unsigned long)queue);
value = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
value &= ~B43legacy_SBF_XFER_REG_BYTESWAP;
b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, value);
qsize = b43legacy_read16(dev, queue->mmio_base
+ B43legacy_PIO_TXQBUFSIZE);
if (qsize == 0) {
b43legacyerr(dev->wl, "This card does not support PIO "
"operation mode. Please use DMA mode "
"(module parameter pio=0).\n");
goto err_freequeue;
}
if (qsize <= B43legacy_PIO_TXQADJUST) {
b43legacyerr(dev->wl, "PIO tx device-queue too small (%u)\n",
qsize);
goto err_freequeue;
}
qsize -= B43legacy_PIO_TXQADJUST;
queue->tx_devq_size = qsize;
setup_txqueues(queue);
out:
return queue;
err_freequeue:
kfree(queue);
queue = NULL;
goto out;
}
static void cancel_transfers(struct b43legacy_pioqueue *queue)
{
struct b43legacy_pio_txpacket *packet, *tmp_packet;
tasklet_disable(&queue->txtask);
list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list)
free_txpacket(packet, 0);
list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list)
free_txpacket(packet, 0);
}
static void b43legacy_destroy_pioqueue(struct b43legacy_pioqueue *queue)
{
if (!queue)
return;
cancel_transfers(queue);
kfree(queue);
}
void b43legacy_pio_free(struct b43legacy_wldev *dev)
{
struct b43legacy_pio *pio;
if (!b43legacy_using_pio(dev))
return;
pio = &dev->pio;
b43legacy_destroy_pioqueue(pio->queue3);
pio->queue3 = NULL;
b43legacy_destroy_pioqueue(pio->queue2);
pio->queue2 = NULL;
b43legacy_destroy_pioqueue(pio->queue1);
pio->queue1 = NULL;
b43legacy_destroy_pioqueue(pio->queue0);
pio->queue0 = NULL;
}
int b43legacy_pio_init(struct b43legacy_wldev *dev)
{
struct b43legacy_pio *pio = &dev->pio;
struct b43legacy_pioqueue *queue;
int err = -ENOMEM;
queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO1_BASE);
if (!queue)
goto out;
pio->queue0 = queue;
queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO2_BASE);
if (!queue)
goto err_destroy0;
pio->queue1 = queue;
queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO3_BASE);
if (!queue)
goto err_destroy1;
pio->queue2 = queue;
queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO4_BASE);
if (!queue)
goto err_destroy2;
pio->queue3 = queue;
if (dev->dev->id.revision < 3)
dev->irq_savedstate |= B43legacy_IRQ_PIO_WORKAROUND;
b43legacydbg(dev->wl, "PIO initialized\n");
err = 0;
out:
return err;
err_destroy2:
b43legacy_destroy_pioqueue(pio->queue2);
pio->queue2 = NULL;
err_destroy1:
b43legacy_destroy_pioqueue(pio->queue1);
pio->queue1 = NULL;
err_destroy0:
b43legacy_destroy_pioqueue(pio->queue0);
pio->queue0 = NULL;
goto out;
}
int b43legacy_pio_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
struct b43legacy_pioqueue *queue = dev->pio.queue1;
struct b43legacy_pio_txpacket *packet;
B43legacy_WARN_ON(queue->tx_suspended);
B43legacy_WARN_ON(list_empty(&queue->txfree));
packet = list_entry(queue->txfree.next, struct b43legacy_pio_txpacket,
list);
packet->skb = skb;
memset(&packet->txstat, 0, sizeof(packet->txstat));
memcpy(&packet->txstat.control, ctl, sizeof(*ctl));
list_move_tail(&packet->list, &queue->txqueue);
queue->nr_txfree--;
queue->nr_tx_packets++;
B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS);
tasklet_schedule(&queue->txtask);
return 0;
}
void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
struct b43legacy_pioqueue *queue;
struct b43legacy_pio_txpacket *packet;
queue = parse_cookie(dev, status->cookie, &packet);
B43legacy_WARN_ON(!queue);
queue->tx_devq_packets--;
queue->tx_devq_used -= (packet->skb->len +
sizeof(struct b43legacy_txhdr_fw3));
if (status->acked)
packet->txstat.flags |= IEEE80211_TX_STATUS_ACK;
packet->txstat.retry_count = status->frame_count - 1;
ieee80211_tx_status_irqsafe(dev->wl->hw, packet->skb,
&(packet->txstat));
packet->skb = NULL;
free_txpacket(packet, 1);
/* If there are packets on the txqueue, poke the tasklet
* to transmit them.
*/
if (!list_empty(&queue->txqueue))
tasklet_schedule(&queue->txtask);
}
void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
struct b43legacy_pio *pio = &dev->pio;
struct b43legacy_pioqueue *queue;
struct ieee80211_tx_queue_stats_data *data;
queue = pio->queue1;
data = &(stats->data[0]);
data->len = B43legacy_PIO_MAXTXPACKETS - queue->nr_txfree;
data->limit = B43legacy_PIO_MAXTXPACKETS;
data->count = queue->nr_tx_packets;
}
static void pio_rx_error(struct b43legacy_pioqueue *queue,
int clear_buffers,
const char *error)
{
int i;
b43legacyerr(queue->dev->wl, "PIO RX error: %s\n", error);
b43legacy_pio_write(queue, B43legacy_PIO_RXCTL,
B43legacy_PIO_RXCTL_READY);
if (clear_buffers) {
B43legacy_WARN_ON(queue->mmio_base != B43legacy_MMIO_PIO1_BASE);
for (i = 0; i < 15; i++) {
/* Dummy read. */
b43legacy_pio_read(queue, B43legacy_PIO_RXDATA);
}
}
}
void b43legacy_pio_rx(struct b43legacy_pioqueue *queue)
{
__le16 preamble[21] = { 0 };
struct b43legacy_rxhdr_fw3 *rxhdr;
u16 tmp;
u16 len;
u16 macstat;
int i;
int preamble_readwords;
struct sk_buff *skb;
tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL);
if (!(tmp & B43legacy_PIO_RXCTL_DATAAVAILABLE))
return;
b43legacy_pio_write(queue, B43legacy_PIO_RXCTL,
B43legacy_PIO_RXCTL_DATAAVAILABLE);
for (i = 0; i < 10; i++) {
tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL);
if (tmp & B43legacy_PIO_RXCTL_READY)
goto data_ready;
udelay(10);
}
b43legacydbg(queue->dev->wl, "PIO RX timed out\n");
return;
data_ready:
len = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA);
if (unlikely(len > 0x700)) {
pio_rx_error(queue, 0, "len > 0x700");
return;
}
if (unlikely(len == 0 && queue->mmio_base !=
B43legacy_MMIO_PIO4_BASE)) {
pio_rx_error(queue, 0, "len == 0");
return;
}
preamble[0] = cpu_to_le16(len);
if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE)
preamble_readwords = 14 / sizeof(u16);
else
preamble_readwords = 18 / sizeof(u16);
for (i = 0; i < preamble_readwords; i++) {
tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA);
preamble[i + 1] = cpu_to_le16(tmp);
}
rxhdr = (struct b43legacy_rxhdr_fw3 *)preamble;
macstat = le16_to_cpu(rxhdr->mac_status);
if (macstat & B43legacy_RX_MAC_FCSERR) {
pio_rx_error(queue,
(queue->mmio_base == B43legacy_MMIO_PIO1_BASE),
"Frame FCS error");
return;
}
if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) {
/* We received an xmit status. */
struct b43legacy_hwtxstatus *hw;
hw = (struct b43legacy_hwtxstatus *)(preamble + 1);
b43legacy_handle_hwtxstatus(queue->dev, hw);
return;
}
skb = dev_alloc_skb(len);
if (unlikely(!skb)) {
pio_rx_error(queue, 1, "OOM");
return;
}
skb_put(skb, len);
for (i = 0; i < len - 1; i += 2) {
tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA);
*((__le16 *)(skb->data + i)) = cpu_to_le16(tmp);
}
if (len % 2) {
tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA);
skb->data[len - 1] = (tmp & 0x00FF);
}
b43legacy_rx(queue->dev, skb, rxhdr);
}
void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue)
{
b43legacy_power_saving_ctl_bits(queue->dev, -1, 1);
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
b43legacy_pio_read(queue, B43legacy_PIO_TXCTL)
| B43legacy_PIO_TXCTL_SUSPEND);
}
void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue)
{
b43legacy_pio_write(queue, B43legacy_PIO_TXCTL,
b43legacy_pio_read(queue, B43legacy_PIO_TXCTL)
& ~B43legacy_PIO_TXCTL_SUSPEND);
b43legacy_power_saving_ctl_bits(queue->dev, -1, -1);
tasklet_schedule(&queue->txtask);
}
void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev)
{
struct b43legacy_pio *pio;
B43legacy_WARN_ON(!b43legacy_using_pio(dev));
pio = &dev->pio;
pio->queue0->tx_frozen = 1;
pio->queue1->tx_frozen = 1;
pio->queue2->tx_frozen = 1;
pio->queue3->tx_frozen = 1;
}
void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev)
{
struct b43legacy_pio *pio;
B43legacy_WARN_ON(!b43legacy_using_pio(dev));
pio = &dev->pio;
pio->queue0->tx_frozen = 0;
pio->queue1->tx_frozen = 0;
pio->queue2->tx_frozen = 0;
pio->queue3->tx_frozen = 0;
if (!list_empty(&pio->queue0->txqueue))
tasklet_schedule(&pio->queue0->txtask);
if (!list_empty(&pio->queue1->txqueue))
tasklet_schedule(&pio->queue1->txtask);
if (!list_empty(&pio->queue2->txqueue))
tasklet_schedule(&pio->queue2->txtask);
if (!list_empty(&pio->queue3->txqueue))
tasklet_schedule(&pio->queue3->txtask);
}
#ifndef B43legacy_PIO_H_
#define B43legacy_PIO_H_
#include "b43legacy.h"
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#define B43legacy_PIO_TXCTL 0x00
#define B43legacy_PIO_TXDATA 0x02
#define B43legacy_PIO_TXQBUFSIZE 0x04
#define B43legacy_PIO_RXCTL 0x08
#define B43legacy_PIO_RXDATA 0x0A
#define B43legacy_PIO_TXCTL_WRITELO (1 << 0)
#define B43legacy_PIO_TXCTL_WRITEHI (1 << 1)
#define B43legacy_PIO_TXCTL_COMPLETE (1 << 2)
#define B43legacy_PIO_TXCTL_INIT (1 << 3)
#define B43legacy_PIO_TXCTL_SUSPEND (1 << 7)
#define B43legacy_PIO_RXCTL_DATAAVAILABLE (1 << 0)
#define B43legacy_PIO_RXCTL_READY (1 << 1)
/* PIO constants */
#define B43legacy_PIO_MAXTXDEVQPACKETS 31
#define B43legacy_PIO_TXQADJUST 80
/* PIO tuning knobs */
#define B43legacy_PIO_MAXTXPACKETS 256
#ifdef CONFIG_B43LEGACY_PIO
struct b43legacy_pioqueue;
struct b43legacy_xmitstatus;
struct b43legacy_pio_txpacket {
struct b43legacy_pioqueue *queue;
struct sk_buff *skb;
struct ieee80211_tx_status txstat;
struct list_head list;
};
#define pio_txpacket_getindex(packet) ((int)((packet) - \
(packet)->queue->tx_packets_cache))
struct b43legacy_pioqueue {
struct b43legacy_wldev *dev;
u16 mmio_base;
bool tx_suspended;
bool tx_frozen;
bool need_workarounds; /* Workarounds needed for core.rev < 3 */
/* Adjusted size of the device internal TX buffer. */
u16 tx_devq_size;
/* Used octets of the device internal TX buffer. */
u16 tx_devq_used;
/* Used packet slots in the device internal TX buffer. */
u8 tx_devq_packets;
/* Packets from the txfree list can
* be taken on incoming TX requests.
*/
struct list_head txfree;
unsigned int nr_txfree;
/* Packets on the txqueue are queued,
* but not completely written to the chip, yet.
*/
struct list_head txqueue;
/* Packets on the txrunning queue are completely
* posted to the device. We are waiting for the txstatus.
*/
struct list_head txrunning;
/* Total number or packets sent.
* (This counter can obviously wrap).
*/
unsigned int nr_tx_packets;
struct tasklet_struct txtask;
struct b43legacy_pio_txpacket
tx_packets_cache[B43legacy_PIO_MAXTXPACKETS];
};
static inline
u16 b43legacy_pio_read(struct b43legacy_pioqueue *queue,
u16 offset)
{
return b43legacy_read16(queue->dev, queue->mmio_base + offset);
}
static inline
void b43legacy_pio_write(struct b43legacy_pioqueue *queue,
u16 offset, u16 value)
{
b43legacy_write16(queue->dev, queue->mmio_base + offset, value);
mmiowb();
}
int b43legacy_pio_init(struct b43legacy_wldev *dev);
void b43legacy_pio_free(struct b43legacy_wldev *dev);
int b43legacy_pio_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl);
void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status);
void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats);
void b43legacy_pio_rx(struct b43legacy_pioqueue *queue);
/* Suspend TX queue in hardware. */
void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue);
void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue);
/* Suspend (freeze) the TX tasklet (software level). */
void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev);
void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev);
#else /* CONFIG_B43LEGACY_PIO */
static inline
int b43legacy_pio_init(struct b43legacy_wldev *dev)
{
return 0;
}
static inline
void b43legacy_pio_free(struct b43legacy_wldev *dev)
{
}
static inline
int b43legacy_pio_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
return 0;
}
static inline
void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
}
static inline
void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
}
static inline
void b43legacy_pio_rx(struct b43legacy_pioqueue *queue)
{
}
static inline
void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue)
{
}
static inline
void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue)
{
}
static inline
void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev)
{
}
static inline
void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev)
{
}
#endif /* CONFIG_B43LEGACY_PIO */
#endif /* B43legacy_PIO_H_ */
/*
Broadcom B43legacy 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>
Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
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 "b43legacy.h"
#include "main.h"
#include "phy.h"
#include "radio.h"
#include "ilt.h"
/* Table for b43legacy_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;
B43legacy_BUG_ON(!((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 inline
u16 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,
};
if (unlikely(channel < 1 || channel > 14)) {
printk(KERN_INFO "b43legacy: Channel %d is out of range\n",
channel);
dump_stack();
return 2412;
}
return frequencies_bg[channel - 1];
}
void b43legacy_radio_lock(struct b43legacy_wldev *dev)
{
u32 status;
status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
status |= B43legacy_SBF_RADIOREG_LOCK;
b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
mmiowb();
udelay(10);
}
void b43legacy_radio_unlock(struct b43legacy_wldev *dev)
{
u32 status;
b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */
status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
status &= ~B43legacy_SBF_RADIOREG_LOCK;
b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
mmiowb();
}
u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset)
{
struct b43legacy_phy *phy = &dev->phy;
switch (phy->type) {
case B43legacy_PHYTYPE_B:
if (phy->radio_ver == 0x2053) {
if (offset < 0x70)
offset += 0x80;
else if (offset < 0x80)
offset += 0x70;
} else if (phy->radio_ver == 0x2050)
offset |= 0x80;
else
B43legacy_WARN_ON(1);
break;
case B43legacy_PHYTYPE_G:
offset |= 0x80;
break;
default:
B43legacy_BUG_ON(1);
}
b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset);
return b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW);
}
void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val)
{
b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset);
mmiowb();
b43legacy_write16(dev, B43legacy_MMIO_RADIO_DATA_LOW, val);
}
static void b43legacy_set_all_gains(struct b43legacy_wldev *dev,
s16 first, s16 second, s16 third)
{
struct b43legacy_phy *phy = &dev->phy;
u16 i;
u16 start = 0x08;
u16 end = 0x18;
u16 offset = 0x0400;
u16 tmp;
if (phy->rev <= 1) {
offset = 0x5000;
start = 0x10;
end = 0x20;
}
for (i = 0; i < 4; i++)
b43legacy_ilt_write(dev, offset + i, first);
for (i = start; i < end; i++)
b43legacy_ilt_write(dev, offset + i, second);
if (third != -1) {
tmp = ((u16)third << 14) | ((u16)third << 6);
b43legacy_phy_write(dev, 0x04A0,
(b43legacy_phy_read(dev, 0x04A0) & 0xBFBF)
| tmp);
b43legacy_phy_write(dev, 0x04A1,
(b43legacy_phy_read(dev, 0x04A1) & 0xBFBF)
| tmp);
b43legacy_phy_write(dev, 0x04A2,
(b43legacy_phy_read(dev, 0x04A2) & 0xBFBF)
| tmp);
}
b43legacy_dummy_transmission(dev);
}
static void b43legacy_set_original_gains(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 i;
u16 tmp;
u16 offset = 0x0400;
u16 start = 0x0008;
u16 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;
b43legacy_ilt_write(dev, offset + i, tmp);
}
for (i = start; i < end; i++)
b43legacy_ilt_write(dev, offset + i, i - start);
b43legacy_phy_write(dev, 0x04A0,
(b43legacy_phy_read(dev, 0x04A0) & 0xBFBF)
| 0x4040);
b43legacy_phy_write(dev, 0x04A1,
(b43legacy_phy_read(dev, 0x04A1) & 0xBFBF)
| 0x4040);
b43legacy_phy_write(dev, 0x04A2,
(b43legacy_phy_read(dev, 0x04A2) & 0xBFBF)
| 0x4000);
b43legacy_dummy_transmission(dev);
}
/* Synthetic PU workaround */
static void b43legacy_synth_pu_workaround(struct b43legacy_wldev *dev,
u8 channel)
{
struct b43legacy_phy *phy = &dev->phy;
might_sleep();
if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6)
/* We do not need the workaround. */
return;
if (channel <= 10)
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
channel2freq_bg(channel + 4));
else
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
channel2freq_bg(channel));
msleep(1);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
channel2freq_bg(channel));
}
u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel)
{
struct b43legacy_phy *phy = &dev->phy;
u8 ret = 0;
u16 saved;
u16 rssi;
u16 temp;
int i;
int j = 0;
saved = b43legacy_phy_read(dev, 0x0403);
b43legacy_radio_selectchannel(dev, channel, 0);
b43legacy_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5);
if (phy->aci_hw_rssi)
rssi = b43legacy_phy_read(dev, 0x048A) & 0x3F;
else
rssi = saved & 0x3F;
/* clamp temp to signed 5bit */
if (rssi > 32)
rssi -= 64;
for (i = 0; i < 100; i++) {
temp = (b43legacy_phy_read(dev, 0x047F) >> 8) & 0x3F;
if (temp > 32)
temp -= 64;
if (temp < rssi)
j++;
if (j >= 20)
ret = 1;
}
b43legacy_phy_write(dev, 0x0403, saved);
return ret;
}
u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u8 ret[13];
unsigned int channel = phy->channel;
unsigned int i;
unsigned int j;
unsigned int start;
unsigned int end;
unsigned long phylock_flags;
if (!((phy->type == B43legacy_PHYTYPE_G) && (phy->rev > 0)))
return 0;
b43legacy_phy_lock(dev, phylock_flags);
b43legacy_radio_lock(dev);
b43legacy_phy_write(dev, 0x0802,
b43legacy_phy_read(dev, 0x0802) & 0xFFFC);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
& 0x7FFF);
b43legacy_set_all_gains(dev, 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] = b43legacy_radio_aci_detect(dev, i);
}
b43legacy_radio_selectchannel(dev, channel, 0);
b43legacy_phy_write(dev, 0x0802,
(b43legacy_phy_read(dev, 0x0802) & 0xFFFC)
| 0x0003);
b43legacy_phy_write(dev, 0x0403,
b43legacy_phy_read(dev, 0x0403) & 0xFFF8);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
| 0x8000);
b43legacy_set_original_gains(dev);
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;
}
b43legacy_radio_unlock(dev);
b43legacy_phy_unlock(dev, phylock_flags);
return ret[channel - 1];
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val)
{
b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset);
mmiowb();
b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_DATA, (u16)val);
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset)
{
u16 val;
b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset);
val = b43legacy_phy_read(dev, B43legacy_PHY_NRSSILT_DATA);
return (s16)val;
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val)
{
u16 i;
s16 tmp;
for (i = 0; i < 64; i++) {
tmp = b43legacy_nrssi_hw_read(dev, i);
tmp -= val;
tmp = limit_value(tmp, -32, 31);
b43legacy_nrssi_hw_write(dev, i, tmp);
}
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
s16 i;
s16 delta;
s32 tmp;
delta = 0x1F - phy->nrssi[0];
for (i = 0; i < 64; i++) {
tmp = (i - delta) * phy->nrssislope;
tmp /= 0x10000;
tmp += 0x3A;
tmp = limit_value(tmp, 0, 0x3F);
phy->nrssi_lt[i] = tmp;
}
}
static void b43legacy_calc_nrssi_offset(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 backup[20] = { 0 };
s16 v47F;
u16 i;
u16 saved = 0xFFFF;
backup[0] = b43legacy_phy_read(dev, 0x0001);
backup[1] = b43legacy_phy_read(dev, 0x0811);
backup[2] = b43legacy_phy_read(dev, 0x0812);
backup[3] = b43legacy_phy_read(dev, 0x0814);
backup[4] = b43legacy_phy_read(dev, 0x0815);
backup[5] = b43legacy_phy_read(dev, 0x005A);
backup[6] = b43legacy_phy_read(dev, 0x0059);
backup[7] = b43legacy_phy_read(dev, 0x0058);
backup[8] = b43legacy_phy_read(dev, 0x000A);
backup[9] = b43legacy_phy_read(dev, 0x0003);
backup[10] = b43legacy_radio_read16(dev, 0x007A);
backup[11] = b43legacy_radio_read16(dev, 0x0043);
b43legacy_phy_write(dev, 0x0429,
b43legacy_phy_read(dev, 0x0429) & 0x7FFF);
b43legacy_phy_write(dev, 0x0001,
(b43legacy_phy_read(dev, 0x0001) & 0x3FFF)
| 0x4000);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811) | 0x000C);
b43legacy_phy_write(dev, 0x0812,
(b43legacy_phy_read(dev, 0x0812) & 0xFFF3)
| 0x0004);
b43legacy_phy_write(dev, 0x0802,
b43legacy_phy_read(dev, 0x0802) & ~(0x1 | 0x2));
if (phy->rev >= 6) {
backup[12] = b43legacy_phy_read(dev, 0x002E);
backup[13] = b43legacy_phy_read(dev, 0x002F);
backup[14] = b43legacy_phy_read(dev, 0x080F);
backup[15] = b43legacy_phy_read(dev, 0x0810);
backup[16] = b43legacy_phy_read(dev, 0x0801);
backup[17] = b43legacy_phy_read(dev, 0x0060);
backup[18] = b43legacy_phy_read(dev, 0x0014);
backup[19] = b43legacy_phy_read(dev, 0x0478);
b43legacy_phy_write(dev, 0x002E, 0);
b43legacy_phy_write(dev, 0x002F, 0);
b43legacy_phy_write(dev, 0x080F, 0);
b43legacy_phy_write(dev, 0x0810, 0);
b43legacy_phy_write(dev, 0x0478,
b43legacy_phy_read(dev, 0x0478) | 0x0100);
b43legacy_phy_write(dev, 0x0801,
b43legacy_phy_read(dev, 0x0801) | 0x0040);
b43legacy_phy_write(dev, 0x0060,
b43legacy_phy_read(dev, 0x0060) | 0x0040);
b43legacy_phy_write(dev, 0x0014,
b43legacy_phy_read(dev, 0x0014) | 0x0200);
}
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A) | 0x0070);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A) | 0x0080);
udelay(30);
v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F == 31) {
for (i = 7; i >= 4; i--) {
b43legacy_radio_write16(dev, 0x007B, i);
udelay(20);
v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8)
& 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F < 31 && saved == 0xFFFF)
saved = i;
}
if (saved == 0xFFFF)
saved = 4;
} else {
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
& 0x007F);
b43legacy_phy_write(dev, 0x0814,
b43legacy_phy_read(dev, 0x0814) | 0x0001);
b43legacy_phy_write(dev, 0x0815,
b43legacy_phy_read(dev, 0x0815) & 0xFFFE);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811) | 0x000C);
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812) | 0x000C);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811) | 0x0030);
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812) | 0x0030);
b43legacy_phy_write(dev, 0x005A, 0x0480);
b43legacy_phy_write(dev, 0x0059, 0x0810);
b43legacy_phy_write(dev, 0x0058, 0x000D);
if (phy->analog == 0)
b43legacy_phy_write(dev, 0x0003, 0x0122);
else
b43legacy_phy_write(dev, 0x000A,
b43legacy_phy_read(dev, 0x000A)
| 0x2000);
b43legacy_phy_write(dev, 0x0814,
b43legacy_phy_read(dev, 0x0814) | 0x0004);
b43legacy_phy_write(dev, 0x0815,
b43legacy_phy_read(dev, 0x0815) & 0xFFFB);
b43legacy_phy_write(dev, 0x0003,
(b43legacy_phy_read(dev, 0x0003) & 0xFF9F)
| 0x0040);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x000F);
b43legacy_set_all_gains(dev, 3, 0, 1);
b43legacy_radio_write16(dev, 0x0043,
(b43legacy_radio_read16(dev, 0x0043)
& 0x00F0) | 0x000F);
udelay(30);
v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F == -32) {
for (i = 0; i < 4; i++) {
b43legacy_radio_write16(dev, 0x007B, i);
udelay(20);
v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >>
8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F > -31 && saved == 0xFFFF)
saved = i;
}
if (saved == 0xFFFF)
saved = 3;
} else
saved = 0;
}
b43legacy_radio_write16(dev, 0x007B, saved);
if (phy->rev >= 6) {
b43legacy_phy_write(dev, 0x002E, backup[12]);
b43legacy_phy_write(dev, 0x002F, backup[13]);
b43legacy_phy_write(dev, 0x080F, backup[14]);
b43legacy_phy_write(dev, 0x0810, backup[15]);
}
b43legacy_phy_write(dev, 0x0814, backup[3]);
b43legacy_phy_write(dev, 0x0815, backup[4]);
b43legacy_phy_write(dev, 0x005A, backup[5]);
b43legacy_phy_write(dev, 0x0059, backup[6]);
b43legacy_phy_write(dev, 0x0058, backup[7]);
b43legacy_phy_write(dev, 0x000A, backup[8]);
b43legacy_phy_write(dev, 0x0003, backup[9]);
b43legacy_radio_write16(dev, 0x0043, backup[11]);
b43legacy_radio_write16(dev, 0x007A, backup[10]);
b43legacy_phy_write(dev, 0x0802,
b43legacy_phy_read(dev, 0x0802) | 0x1 | 0x2);
b43legacy_phy_write(dev, 0x0429,
b43legacy_phy_read(dev, 0x0429) | 0x8000);
b43legacy_set_original_gains(dev);
if (phy->rev >= 6) {
b43legacy_phy_write(dev, 0x0801, backup[16]);
b43legacy_phy_write(dev, 0x0060, backup[17]);
b43legacy_phy_write(dev, 0x0014, backup[18]);
b43legacy_phy_write(dev, 0x0478, backup[19]);
}
b43legacy_phy_write(dev, 0x0001, backup[0]);
b43legacy_phy_write(dev, 0x0812, backup[2]);
b43legacy_phy_write(dev, 0x0811, backup[1]);
}
void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 backup[18] = { 0 };
u16 tmp;
s16 nrssi0;
s16 nrssi1;
switch (phy->type) {
case B43legacy_PHYTYPE_B:
backup[0] = b43legacy_radio_read16(dev, 0x007A);
backup[1] = b43legacy_radio_read16(dev, 0x0052);
backup[2] = b43legacy_radio_read16(dev, 0x0043);
backup[3] = b43legacy_phy_read(dev, 0x0030);
backup[4] = b43legacy_phy_read(dev, 0x0026);
backup[5] = b43legacy_phy_read(dev, 0x0015);
backup[6] = b43legacy_phy_read(dev, 0x002A);
backup[7] = b43legacy_phy_read(dev, 0x0020);
backup[8] = b43legacy_phy_read(dev, 0x005A);
backup[9] = b43legacy_phy_read(dev, 0x0059);
backup[10] = b43legacy_phy_read(dev, 0x0058);
backup[11] = b43legacy_read16(dev, 0x03E2);
backup[12] = b43legacy_read16(dev, 0x03E6);
backup[13] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
tmp = b43legacy_radio_read16(dev, 0x007A);
tmp &= (phy->rev >= 5) ? 0x007F : 0x000F;
b43legacy_radio_write16(dev, 0x007A, tmp);
b43legacy_phy_write(dev, 0x0030, 0x00FF);
b43legacy_write16(dev, 0x03EC, 0x7F7F);
b43legacy_phy_write(dev, 0x0026, 0x0000);
b43legacy_phy_write(dev, 0x0015,
b43legacy_phy_read(dev, 0x0015) | 0x0020);
b43legacy_phy_write(dev, 0x002A, 0x08A3);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x0080);
nrssi0 = (s16)b43legacy_phy_read(dev, 0x0027);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
& 0x007F);
if (phy->analog >= 2)
b43legacy_write16(dev, 0x03E6, 0x0040);
else if (phy->analog == 0)
b43legacy_write16(dev, 0x03E6, 0x0122);
else
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
b43legacy_read16(dev,
B43legacy_MMIO_CHANNEL_EXT) & 0x2000);
b43legacy_phy_write(dev, 0x0020, 0x3F3F);
b43legacy_phy_write(dev, 0x0015, 0xF330);
b43legacy_radio_write16(dev, 0x005A, 0x0060);
b43legacy_radio_write16(dev, 0x0043,
b43legacy_radio_read16(dev, 0x0043)
& 0x00F0);
b43legacy_phy_write(dev, 0x005A, 0x0480);
b43legacy_phy_write(dev, 0x0059, 0x0810);
b43legacy_phy_write(dev, 0x0058, 0x000D);
udelay(20);
nrssi1 = (s16)b43legacy_phy_read(dev, 0x0027);
b43legacy_phy_write(dev, 0x0030, backup[3]);
b43legacy_radio_write16(dev, 0x007A, backup[0]);
b43legacy_write16(dev, 0x03E2, backup[11]);
b43legacy_phy_write(dev, 0x0026, backup[4]);
b43legacy_phy_write(dev, 0x0015, backup[5]);
b43legacy_phy_write(dev, 0x002A, backup[6]);
b43legacy_synth_pu_workaround(dev, phy->channel);
if (phy->analog != 0)
b43legacy_write16(dev, 0x03F4, backup[13]);
b43legacy_phy_write(dev, 0x0020, backup[7]);
b43legacy_phy_write(dev, 0x005A, backup[8]);
b43legacy_phy_write(dev, 0x0059, backup[9]);
b43legacy_phy_write(dev, 0x0058, backup[10]);
b43legacy_radio_write16(dev, 0x0052, backup[1]);
b43legacy_radio_write16(dev, 0x0043, backup[2]);
if (nrssi0 == nrssi1)
phy->nrssislope = 0x00010000;
else
phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
if (nrssi0 <= -4) {
phy->nrssi[0] = nrssi0;
phy->nrssi[1] = nrssi1;
}
break;
case B43legacy_PHYTYPE_G:
if (phy->radio_rev >= 9)
return;
if (phy->radio_rev == 8)
b43legacy_calc_nrssi_offset(dev);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
& 0x7FFF);
b43legacy_phy_write(dev, 0x0802,
b43legacy_phy_read(dev, 0x0802) & 0xFFFC);
backup[7] = b43legacy_read16(dev, 0x03E2);
b43legacy_write16(dev, 0x03E2,
b43legacy_read16(dev, 0x03E2) | 0x8000);
backup[0] = b43legacy_radio_read16(dev, 0x007A);
backup[1] = b43legacy_radio_read16(dev, 0x0052);
backup[2] = b43legacy_radio_read16(dev, 0x0043);
backup[3] = b43legacy_phy_read(dev, 0x0015);
backup[4] = b43legacy_phy_read(dev, 0x005A);
backup[5] = b43legacy_phy_read(dev, 0x0059);
backup[6] = b43legacy_phy_read(dev, 0x0058);
backup[8] = b43legacy_read16(dev, 0x03E6);
backup[9] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
if (phy->rev >= 3) {
backup[10] = b43legacy_phy_read(dev, 0x002E);
backup[11] = b43legacy_phy_read(dev, 0x002F);
backup[12] = b43legacy_phy_read(dev, 0x080F);
backup[13] = b43legacy_phy_read(dev,
B43legacy_PHY_G_LO_CONTROL);
backup[14] = b43legacy_phy_read(dev, 0x0801);
backup[15] = b43legacy_phy_read(dev, 0x0060);
backup[16] = b43legacy_phy_read(dev, 0x0014);
backup[17] = b43legacy_phy_read(dev, 0x0478);
b43legacy_phy_write(dev, 0x002E, 0);
b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, 0);
switch (phy->rev) {
case 4: case 6: case 7:
b43legacy_phy_write(dev, 0x0478,
b43legacy_phy_read(dev,
0x0478) | 0x0100);
b43legacy_phy_write(dev, 0x0801,
b43legacy_phy_read(dev,
0x0801) | 0x0040);
break;
case 3: case 5:
b43legacy_phy_write(dev, 0x0801,
b43legacy_phy_read(dev,
0x0801) & 0xFFBF);
break;
}
b43legacy_phy_write(dev, 0x0060,
b43legacy_phy_read(dev, 0x0060)
| 0x0040);
b43legacy_phy_write(dev, 0x0014,
b43legacy_phy_read(dev, 0x0014)
| 0x0200);
}
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x0070);
b43legacy_set_all_gains(dev, 0, 8, 0);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
& 0x00F7);
if (phy->rev >= 2) {
b43legacy_phy_write(dev, 0x0811,
(b43legacy_phy_read(dev, 0x0811)
& 0xFFCF) | 0x0030);
b43legacy_phy_write(dev, 0x0812,
(b43legacy_phy_read(dev, 0x0812)
& 0xFFCF) | 0x0010);
}
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x0080);
udelay(20);
nrssi0 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
if (nrssi0 >= 0x0020)
nrssi0 -= 0x0040;
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
& 0x007F);
if (phy->analog >= 2)
b43legacy_phy_write(dev, 0x0003,
(b43legacy_phy_read(dev, 0x0003)
& 0xFF9F) | 0x0040);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
b43legacy_read16(dev,
B43legacy_MMIO_CHANNEL_EXT) | 0x2000);
b43legacy_radio_write16(dev, 0x007A,
b43legacy_radio_read16(dev, 0x007A)
| 0x000F);
b43legacy_phy_write(dev, 0x0015, 0xF330);
if (phy->rev >= 2) {
b43legacy_phy_write(dev, 0x0812,
(b43legacy_phy_read(dev, 0x0812)
& 0xFFCF) | 0x0020);
b43legacy_phy_write(dev, 0x0811,
(b43legacy_phy_read(dev, 0x0811)
& 0xFFCF) | 0x0020);
}
b43legacy_set_all_gains(dev, 3, 0, 1);
if (phy->radio_rev == 8)
b43legacy_radio_write16(dev, 0x0043, 0x001F);
else {
tmp = b43legacy_radio_read16(dev, 0x0052) & 0xFF0F;
b43legacy_radio_write16(dev, 0x0052, tmp | 0x0060);
tmp = b43legacy_radio_read16(dev, 0x0043) & 0xFFF0;
b43legacy_radio_write16(dev, 0x0043, tmp | 0x0009);
}
b43legacy_phy_write(dev, 0x005A, 0x0480);
b43legacy_phy_write(dev, 0x0059, 0x0810);
b43legacy_phy_write(dev, 0x0058, 0x000D);
udelay(20);
nrssi1 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
if (nrssi1 >= 0x0020)
nrssi1 -= 0x0040;
if (nrssi0 == nrssi1)
phy->nrssislope = 0x00010000;
else
phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
if (nrssi0 >= -4) {
phy->nrssi[0] = nrssi1;
phy->nrssi[1] = nrssi0;
}
if (phy->rev >= 3) {
b43legacy_phy_write(dev, 0x002E, backup[10]);
b43legacy_phy_write(dev, 0x002F, backup[11]);
b43legacy_phy_write(dev, 0x080F, backup[12]);
b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL,
backup[13]);
}
if (phy->rev >= 2) {
b43legacy_phy_write(dev, 0x0812,
b43legacy_phy_read(dev, 0x0812)
& 0xFFCF);
b43legacy_phy_write(dev, 0x0811,
b43legacy_phy_read(dev, 0x0811)
& 0xFFCF);
}
b43legacy_radio_write16(dev, 0x007A, backup[0]);
b43legacy_radio_write16(dev, 0x0052, backup[1]);
b43legacy_radio_write16(dev, 0x0043, backup[2]);
b43legacy_write16(dev, 0x03E2, backup[7]);
b43legacy_write16(dev, 0x03E6, backup[8]);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[9]);
b43legacy_phy_write(dev, 0x0015, backup[3]);
b43legacy_phy_write(dev, 0x005A, backup[4]);
b43legacy_phy_write(dev, 0x0059, backup[5]);
b43legacy_phy_write(dev, 0x0058, backup[6]);
b43legacy_synth_pu_workaround(dev, phy->channel);
b43legacy_phy_write(dev, 0x0802,
b43legacy_phy_read(dev, 0x0802) | 0x0003);
b43legacy_set_original_gains(dev);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
| 0x8000);
if (phy->rev >= 3) {
b43legacy_phy_write(dev, 0x0801, backup[14]);
b43legacy_phy_write(dev, 0x0060, backup[15]);
b43legacy_phy_write(dev, 0x0014, backup[16]);
b43legacy_phy_write(dev, 0x0478, backup[17]);
}
b43legacy_nrssi_mem_update(dev);
b43legacy_calc_nrssi_threshold(dev);
break;
default:
B43legacy_BUG_ON(1);
}
}
void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
s32 threshold;
s32 a;
s32 b;
s16 tmp16;
u16 tmp_u16;
switch (phy->type) {
case B43legacy_PHYTYPE_B: {
if (phy->radio_ver != 0x2050)
return;
if (!(dev->dev->bus->sprom.r1.boardflags_lo &
B43legacy_BFL_RSSI))
return;
if (phy->radio_rev >= 6) {
threshold = (phy->nrssi[1] - phy->nrssi[0]) * 32;
threshold += 20 * (phy->nrssi[0] + 1);
threshold /= 40;
} else
threshold = phy->nrssi[1] - 5;
threshold = limit_value(threshold, 0, 0x3E);
b43legacy_phy_read(dev, 0x0020); /* dummy read */
b43legacy_phy_write(dev, 0x0020, (((u16)threshold) << 8)
| 0x001C);
if (phy->radio_rev >= 6) {
b43legacy_phy_write(dev, 0x0087, 0x0E0D);
b43legacy_phy_write(dev, 0x0086, 0x0C0B);
b43legacy_phy_write(dev, 0x0085, 0x0A09);
b43legacy_phy_write(dev, 0x0084, 0x0808);
b43legacy_phy_write(dev, 0x0083, 0x0808);
b43legacy_phy_write(dev, 0x0082, 0x0604);
b43legacy_phy_write(dev, 0x0081, 0x0302);
b43legacy_phy_write(dev, 0x0080, 0x0100);
}
break;
}
case B43legacy_PHYTYPE_G:
if (!phy->gmode ||
!(dev->dev->bus->sprom.r1.boardflags_lo &
B43legacy_BFL_RSSI)) {
tmp16 = b43legacy_nrssi_hw_read(dev, 0x20);
if (tmp16 >= 0x20)
tmp16 -= 0x40;
if (tmp16 < 3)
b43legacy_phy_write(dev, 0x048A,
(b43legacy_phy_read(dev,
0x048A) & 0xF000) | 0x09EB);
else
b43legacy_phy_write(dev, 0x048A,
(b43legacy_phy_read(dev,
0x048A) & 0xF000) | 0x0AED);
} else {
if (phy->interfmode ==
B43legacy_RADIO_INTERFMODE_NONWLAN) {
a = 0xE;
b = 0xA;
} else if (!phy->aci_wlan_automatic &&
phy->aci_enable) {
a = 0x13;
b = 0x12;
} else {
a = 0xE;
b = 0x11;
}
a = a * (phy->nrssi[1] - phy->nrssi[0]);
a += (phy->nrssi[0] << 6);
if (a < 32)
a += 31;
else
a += 32;
a = a >> 6;
a = limit_value(a, -31, 31);
b = b * (phy->nrssi[1] - phy->nrssi[0]);
b += (phy->nrssi[0] << 6);
if (b < 32)
b += 31;
else
b += 32;
b = b >> 6;
b = limit_value(b, -31, 31);
tmp_u16 = b43legacy_phy_read(dev, 0x048A) & 0xF000;
tmp_u16 |= ((u32)b & 0x0000003F);
tmp_u16 |= (((u32)a & 0x0000003F) << 6);
b43legacy_phy_write(dev, 0x048A, tmp_u16);
}
break;
default:
B43legacy_BUG_ON(1);
}
}
/* Stack implementation to save/restore values from the
* interference mitigation code.
* It is save to restore values in random order.
*/
static void _stack_save(u32 *_stackptr, size_t *stackidx,
u8 id, u16 offset, u16 value)
{
u32 *stackptr = &(_stackptr[*stackidx]);
B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000));
B43legacy_WARN_ON(!((id & 0xF8) == 0x00));
*stackptr = offset;
*stackptr |= ((u32)id) << 13;
*stackptr |= ((u32)value) << 16;
(*stackidx)++;
B43legacy_WARN_ON(!(*stackidx < B43legacy_INTERFSTACK_SIZE));
}
static u16 _stack_restore(u32 *stackptr,
u8 id, u16 offset)
{
size_t i;
B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000));
B43legacy_WARN_ON(!((id & 0xF8) == 0x00));
for (i = 0; i < B43legacy_INTERFSTACK_SIZE; i++, stackptr++) {
if ((*stackptr & 0x00001FFF) != offset)
continue;
if (((*stackptr & 0x00007000) >> 13) != id)
continue;
return ((*stackptr & 0xFFFF0000) >> 16);
}
B43legacy_BUG_ON(1);
return 0;
}
#define phy_stacksave(offset) \
do { \
_stack_save(stack, &stackidx, 0x1, (offset), \
b43legacy_phy_read(dev, (offset))); \
} while (0)
#define phy_stackrestore(offset) \
do { \
b43legacy_phy_write(dev, (offset), \
_stack_restore(stack, 0x1, \
(offset))); \
} while (0)
#define radio_stacksave(offset) \
do { \
_stack_save(stack, &stackidx, 0x2, (offset), \
b43legacy_radio_read16(dev, (offset))); \
} while (0)
#define radio_stackrestore(offset) \
do { \
b43legacy_radio_write16(dev, (offset), \
_stack_restore(stack, 0x2, \
(offset))); \
} while (0)
#define ilt_stacksave(offset) \
do { \
_stack_save(stack, &stackidx, 0x3, (offset), \
b43legacy_ilt_read(dev, (offset))); \
} while (0)
#define ilt_stackrestore(offset) \
do { \
b43legacy_ilt_write(dev, (offset), \
_stack_restore(stack, 0x3, \
(offset))); \
} while (0)
static void
b43legacy_radio_interference_mitigation_enable(struct b43legacy_wldev *dev,
int mode)
{
struct b43legacy_phy *phy = &dev->phy;
u16 tmp;
u16 flipped;
u32 tmp32;
size_t stackidx = 0;
u32 *stack = phy->interfstack;
switch (mode) {
case B43legacy_RADIO_INTERFMODE_NONWLAN:
if (phy->rev != 1) {
b43legacy_phy_write(dev, 0x042B,
b43legacy_phy_read(dev, 0x042B)
| 0x0800);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev,
B43legacy_PHY_G_CRS) & ~0x4000);
break;
}
radio_stacksave(0x0078);
tmp = (b43legacy_radio_read16(dev, 0x0078) & 0x001E);
flipped = flip_4bit(tmp);
if (flipped < 10 && flipped >= 8)
flipped = 7;
else if (flipped >= 10)
flipped -= 3;
flipped = flip_4bit(flipped);
flipped = (flipped << 1) | 0x0020;
b43legacy_radio_write16(dev, 0x0078, flipped);
b43legacy_calc_nrssi_threshold(dev);
phy_stacksave(0x0406);
b43legacy_phy_write(dev, 0x0406, 0x7E28);
b43legacy_phy_write(dev, 0x042B,
b43legacy_phy_read(dev, 0x042B) | 0x0800);
b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
b43legacy_phy_read(dev,
B43legacy_PHY_RADIO_BITFIELD) | 0x1000);
phy_stacksave(0x04A0);
b43legacy_phy_write(dev, 0x04A0,
(b43legacy_phy_read(dev, 0x04A0) & 0xC0C0)
| 0x0008);
phy_stacksave(0x04A1);
b43legacy_phy_write(dev, 0x04A1,
(b43legacy_phy_read(dev, 0x04A1) & 0xC0C0)
| 0x0605);
phy_stacksave(0x04A2);
b43legacy_phy_write(dev, 0x04A2,
(b43legacy_phy_read(dev, 0x04A2) & 0xC0C0)
| 0x0204);
phy_stacksave(0x04A8);
b43legacy_phy_write(dev, 0x04A8,
(b43legacy_phy_read(dev, 0x04A8) & 0xC0C0)
| 0x0803);
phy_stacksave(0x04AB);
b43legacy_phy_write(dev, 0x04AB,
(b43legacy_phy_read(dev, 0x04AB) & 0xC0C0)
| 0x0605);
phy_stacksave(0x04A7);
b43legacy_phy_write(dev, 0x04A7, 0x0002);
phy_stacksave(0x04A3);
b43legacy_phy_write(dev, 0x04A3, 0x287A);
phy_stacksave(0x04A9);
b43legacy_phy_write(dev, 0x04A9, 0x2027);
phy_stacksave(0x0493);
b43legacy_phy_write(dev, 0x0493, 0x32F5);
phy_stacksave(0x04AA);
b43legacy_phy_write(dev, 0x04AA, 0x2027);
phy_stacksave(0x04AC);
b43legacy_phy_write(dev, 0x04AC, 0x32F5);
break;
case B43legacy_RADIO_INTERFMODE_MANUALWLAN:
if (b43legacy_phy_read(dev, 0x0033) & 0x0800)
break;
phy->aci_enable = 1;
phy_stacksave(B43legacy_PHY_RADIO_BITFIELD);
phy_stacksave(B43legacy_PHY_G_CRS);
if (phy->rev < 2)
phy_stacksave(0x0406);
else {
phy_stacksave(0x04C0);
phy_stacksave(0x04C1);
}
phy_stacksave(0x0033);
phy_stacksave(0x04A7);
phy_stacksave(0x04A3);
phy_stacksave(0x04A9);
phy_stacksave(0x04AA);
phy_stacksave(0x04AC);
phy_stacksave(0x0493);
phy_stacksave(0x04A1);
phy_stacksave(0x04A0);
phy_stacksave(0x04A2);
phy_stacksave(0x048A);
phy_stacksave(0x04A8);
phy_stacksave(0x04AB);
if (phy->rev == 2) {
phy_stacksave(0x04AD);
phy_stacksave(0x04AE);
} else if (phy->rev >= 3) {
phy_stacksave(0x04AD);
phy_stacksave(0x0415);
phy_stacksave(0x0416);
phy_stacksave(0x0417);
ilt_stacksave(0x1A00 + 0x2);
ilt_stacksave(0x1A00 + 0x3);
}
phy_stacksave(0x042B);
phy_stacksave(0x048C);
b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
b43legacy_phy_read(dev,
B43legacy_PHY_RADIO_BITFIELD) & ~0x1000);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
(b43legacy_phy_read(dev,
B43legacy_PHY_G_CRS)
& 0xFFFC) | 0x0002);
b43legacy_phy_write(dev, 0x0033, 0x0800);
b43legacy_phy_write(dev, 0x04A3, 0x2027);
b43legacy_phy_write(dev, 0x04A9, 0x1CA8);
b43legacy_phy_write(dev, 0x0493, 0x287A);
b43legacy_phy_write(dev, 0x04AA, 0x1CA8);
b43legacy_phy_write(dev, 0x04AC, 0x287A);
b43legacy_phy_write(dev, 0x04A0,
(b43legacy_phy_read(dev, 0x04A0)
& 0xFFC0) | 0x001A);
b43legacy_phy_write(dev, 0x04A7, 0x000D);
if (phy->rev < 2)
b43legacy_phy_write(dev, 0x0406, 0xFF0D);
else if (phy->rev == 2) {
b43legacy_phy_write(dev, 0x04C0, 0xFFFF);
b43legacy_phy_write(dev, 0x04C1, 0x00A9);
} else {
b43legacy_phy_write(dev, 0x04C0, 0x00C1);
b43legacy_phy_write(dev, 0x04C1, 0x0059);
}
b43legacy_phy_write(dev, 0x04A1,
(b43legacy_phy_read(dev, 0x04A1)
& 0xC0FF) | 0x1800);
b43legacy_phy_write(dev, 0x04A1,
(b43legacy_phy_read(dev, 0x04A1)
& 0xFFC0) | 0x0015);
b43legacy_phy_write(dev, 0x04A8,
(b43legacy_phy_read(dev, 0x04A8)
& 0xCFFF) | 0x1000);
b43legacy_phy_write(dev, 0x04A8,
(b43legacy_phy_read(dev, 0x04A8)
& 0xF0FF) | 0x0A00);
b43legacy_phy_write(dev, 0x04AB,
(b43legacy_phy_read(dev, 0x04AB)
& 0xCFFF) | 0x1000);
b43legacy_phy_write(dev, 0x04AB,
(b43legacy_phy_read(dev, 0x04AB)
& 0xF0FF) | 0x0800);
b43legacy_phy_write(dev, 0x04AB,
(b43legacy_phy_read(dev, 0x04AB)
& 0xFFCF) | 0x0010);
b43legacy_phy_write(dev, 0x04AB,
(b43legacy_phy_read(dev, 0x04AB)
& 0xFFF0) | 0x0005);
b43legacy_phy_write(dev, 0x04A8,
(b43legacy_phy_read(dev, 0x04A8)
& 0xFFCF) | 0x0010);
b43legacy_phy_write(dev, 0x04A8,
(b43legacy_phy_read(dev, 0x04A8)
& 0xFFF0) | 0x0006);
b43legacy_phy_write(dev, 0x04A2,
(b43legacy_phy_read(dev, 0x04A2)
& 0xF0FF) | 0x0800);
b43legacy_phy_write(dev, 0x04A0,
(b43legacy_phy_read(dev, 0x04A0)
& 0xF0FF) | 0x0500);
b43legacy_phy_write(dev, 0x04A2,
(b43legacy_phy_read(dev, 0x04A2)
& 0xFFF0) | 0x000B);
if (phy->rev >= 3) {
b43legacy_phy_write(dev, 0x048A,
b43legacy_phy_read(dev, 0x048A)
& ~0x8000);
b43legacy_phy_write(dev, 0x0415,
(b43legacy_phy_read(dev, 0x0415)
& 0x8000) | 0x36D8);
b43legacy_phy_write(dev, 0x0416,
(b43legacy_phy_read(dev, 0x0416)
& 0x8000) | 0x36D8);
b43legacy_phy_write(dev, 0x0417,
(b43legacy_phy_read(dev, 0x0417)
& 0xFE00) | 0x016D);
} else {
b43legacy_phy_write(dev, 0x048A,
b43legacy_phy_read(dev, 0x048A)
| 0x1000);
b43legacy_phy_write(dev, 0x048A,
(b43legacy_phy_read(dev, 0x048A)
& 0x9FFF) | 0x2000);
tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET);
if (!(tmp32 & 0x800)) {
tmp32 |= 0x800;
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
tmp32);
}
}
if (phy->rev >= 2)
b43legacy_phy_write(dev, 0x042B,
b43legacy_phy_read(dev, 0x042B)
| 0x0800);
b43legacy_phy_write(dev, 0x048C,
(b43legacy_phy_read(dev, 0x048C)
& 0xF0FF) | 0x0200);
if (phy->rev == 2) {
b43legacy_phy_write(dev, 0x04AE,
(b43legacy_phy_read(dev, 0x04AE)
& 0xFF00) | 0x007F);
b43legacy_phy_write(dev, 0x04AD,
(b43legacy_phy_read(dev, 0x04AD)
& 0x00FF) | 0x1300);
} else if (phy->rev >= 6) {
b43legacy_ilt_write(dev, 0x1A00 + 0x3, 0x007F);
b43legacy_ilt_write(dev, 0x1A00 + 0x2, 0x007F);
b43legacy_phy_write(dev, 0x04AD,
b43legacy_phy_read(dev, 0x04AD)
& 0x00FF);
}
b43legacy_calc_nrssi_slope(dev);
break;
default:
B43legacy_BUG_ON(1);
}
}
static void
b43legacy_radio_interference_mitigation_disable(struct b43legacy_wldev *dev,
int mode)
{
struct b43legacy_phy *phy = &dev->phy;
u32 tmp32;
u32 *stack = phy->interfstack;
switch (mode) {
case B43legacy_RADIO_INTERFMODE_NONWLAN:
if (phy->rev != 1) {
b43legacy_phy_write(dev, 0x042B,
b43legacy_phy_read(dev, 0x042B)
& ~0x0800);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev,
B43legacy_PHY_G_CRS) | 0x4000);
break;
}
phy_stackrestore(0x0078);
b43legacy_calc_nrssi_threshold(dev);
phy_stackrestore(0x0406);
b43legacy_phy_write(dev, 0x042B,
b43legacy_phy_read(dev, 0x042B) & ~0x0800);
if (!dev->bad_frames_preempt)
b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
b43legacy_phy_read(dev,
B43legacy_PHY_RADIO_BITFIELD)
& ~(1 << 11));
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
| 0x4000);
phy_stackrestore(0x04A0);
phy_stackrestore(0x04A1);
phy_stackrestore(0x04A2);
phy_stackrestore(0x04A8);
phy_stackrestore(0x04AB);
phy_stackrestore(0x04A7);
phy_stackrestore(0x04A3);
phy_stackrestore(0x04A9);
phy_stackrestore(0x0493);
phy_stackrestore(0x04AA);
phy_stackrestore(0x04AC);
break;
case B43legacy_RADIO_INTERFMODE_MANUALWLAN:
if (!(b43legacy_phy_read(dev, 0x0033) & 0x0800))
break;
phy->aci_enable = 0;
phy_stackrestore(B43legacy_PHY_RADIO_BITFIELD);
phy_stackrestore(B43legacy_PHY_G_CRS);
phy_stackrestore(0x0033);
phy_stackrestore(0x04A3);
phy_stackrestore(0x04A9);
phy_stackrestore(0x0493);
phy_stackrestore(0x04AA);
phy_stackrestore(0x04AC);
phy_stackrestore(0x04A0);
phy_stackrestore(0x04A7);
if (phy->rev >= 2) {
phy_stackrestore(0x04C0);
phy_stackrestore(0x04C1);
} else
phy_stackrestore(0x0406);
phy_stackrestore(0x04A1);
phy_stackrestore(0x04AB);
phy_stackrestore(0x04A8);
if (phy->rev == 2) {
phy_stackrestore(0x04AD);
phy_stackrestore(0x04AE);
} else if (phy->rev >= 3) {
phy_stackrestore(0x04AD);
phy_stackrestore(0x0415);
phy_stackrestore(0x0416);
phy_stackrestore(0x0417);
ilt_stackrestore(0x1A00 + 0x2);
ilt_stackrestore(0x1A00 + 0x3);
}
phy_stackrestore(0x04A2);
phy_stackrestore(0x04A8);
phy_stackrestore(0x042B);
phy_stackrestore(0x048C);
tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET);
if (tmp32 & 0x800) {
tmp32 &= ~0x800;
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
tmp32);
}
b43legacy_calc_nrssi_slope(dev);
break;
default:
B43legacy_BUG_ON(1);
}
}
#undef phy_stacksave
#undef phy_stackrestore
#undef radio_stacksave
#undef radio_stackrestore
#undef ilt_stacksave
#undef ilt_stackrestore
int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev,
int mode)
{
struct b43legacy_phy *phy = &dev->phy;
int currentmode;
if ((phy->type != B43legacy_PHYTYPE_G) ||
(phy->rev == 0) || (!phy->gmode))
return -ENODEV;
phy->aci_wlan_automatic = 0;
switch (mode) {
case B43legacy_RADIO_INTERFMODE_AUTOWLAN:
phy->aci_wlan_automatic = 1;
if (phy->aci_enable)
mode = B43legacy_RADIO_INTERFMODE_MANUALWLAN;
else
mode = B43legacy_RADIO_INTERFMODE_NONE;
break;
case B43legacy_RADIO_INTERFMODE_NONE:
case B43legacy_RADIO_INTERFMODE_NONWLAN:
case B43legacy_RADIO_INTERFMODE_MANUALWLAN:
break;
default:
return -EINVAL;
}
currentmode = phy->interfmode;
if (currentmode == mode)
return 0;
if (currentmode != B43legacy_RADIO_INTERFMODE_NONE)
b43legacy_radio_interference_mitigation_disable(dev,
currentmode);
if (mode == B43legacy_RADIO_INTERFMODE_NONE) {
phy->aci_enable = 0;
phy->aci_hw_rssi = 0;
} else
b43legacy_radio_interference_mitigation_enable(dev, mode);
phy->interfmode = mode;
return 0;
}
u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev)
{
u16 reg;
u16 index;
u16 ret;
reg = b43legacy_radio_read16(dev, 0x0060);
index = (reg & 0x001E) >> 1;
ret = rcc_table[index] << 1;
ret |= (reg & 0x0001);
ret |= 0x0020;
return ret;
}
#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0))
static u16 b43legacy_get_812_value(struct b43legacy_wldev *dev, u8 lpd)
{
struct b43legacy_phy *phy = &dev->phy;
u16 loop_or = 0;
u16 adj_loopback_gain = phy->loopback_gain[0];
u8 loop;
u16 extern_lna_control;
if (!phy->gmode)
return 0;
if (!has_loopback_gain(phy)) {
if (phy->rev < 7 || !(dev->dev->bus->sprom.r1.boardflags_lo
& B43legacy_BFL_EXTLNA)) {
switch (lpd) {
case LPD(0, 1, 1):
return 0x0FB2;
case LPD(0, 0, 1):
return 0x00B2;
case LPD(1, 0, 1):
return 0x30B2;
case LPD(1, 0, 0):
return 0x30B3;
default:
B43legacy_BUG_ON(1);
}
} else {
switch (lpd) {
case LPD(0, 1, 1):
return 0x8FB2;
case LPD(0, 0, 1):
return 0x80B2;
case LPD(1, 0, 1):
return 0x20B2;
case LPD(1, 0, 0):
return 0x20B3;
default:
B43legacy_BUG_ON(1);
}
}
} else {
if (phy->radio_rev == 8)
adj_loopback_gain += 0x003E;
else
adj_loopback_gain += 0x0026;
if (adj_loopback_gain >= 0x46) {
adj_loopback_gain -= 0x46;
extern_lna_control = 0x3000;
} else if (adj_loopback_gain >= 0x3A) {
adj_loopback_gain -= 0x3A;
extern_lna_control = 0x2000;
} else if (adj_loopback_gain >= 0x2E) {
adj_loopback_gain -= 0x2E;
extern_lna_control = 0x1000;
} else {
adj_loopback_gain -= 0x10;
extern_lna_control = 0x0000;
}
for (loop = 0; loop < 16; loop++) {
u16 tmp = adj_loopback_gain - 6 * loop;
if (tmp < 6)
break;
}
loop_or = (loop << 8) | extern_lna_control;
if (phy->rev >= 7 && dev->dev->bus->sprom.r1.boardflags_lo
& B43legacy_BFL_EXTLNA) {
if (extern_lna_control)
loop_or |= 0x8000;
switch (lpd) {
case LPD(0, 1, 1):
return 0x8F92;
case LPD(0, 0, 1):
return (0x8092 | loop_or);
case LPD(1, 0, 1):
return (0x2092 | loop_or);
case LPD(1, 0, 0):
return (0x2093 | loop_or);
default:
B43legacy_BUG_ON(1);
}
} else {
switch (lpd) {
case LPD(0, 1, 1):
return 0x0F92;
case LPD(0, 0, 1):
case LPD(1, 0, 1):
return (0x0092 | loop_or);
case LPD(1, 0, 0):
return (0x0093 | loop_or);
default:
B43legacy_BUG_ON(1);
}
}
}
return 0;
}
u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 backup[21] = { 0 };
u16 ret;
u16 i;
u16 j;
u32 tmp1 = 0;
u32 tmp2 = 0;
backup[0] = b43legacy_radio_read16(dev, 0x0043);
backup[14] = b43legacy_radio_read16(dev, 0x0051);
backup[15] = b43legacy_radio_read16(dev, 0x0052);
backup[1] = b43legacy_phy_read(dev, 0x0015);
backup[16] = b43legacy_phy_read(dev, 0x005A);
backup[17] = b43legacy_phy_read(dev, 0x0059);
backup[18] = b43legacy_phy_read(dev, 0x0058);
if (phy->type == B43legacy_PHYTYPE_B) {
backup[2] = b43legacy_phy_read(dev, 0x0030);
backup[3] = b43legacy_read16(dev, 0x03EC);
b43legacy_phy_write(dev, 0x0030, 0x00FF);
b43legacy_write16(dev, 0x03EC, 0x3F3F);
} else {
if (phy->gmode) {
backup[4] = b43legacy_phy_read(dev, 0x0811);
backup[5] = b43legacy_phy_read(dev, 0x0812);
backup[6] = b43legacy_phy_read(dev, 0x0814);
backup[7] = b43legacy_phy_read(dev, 0x0815);
backup[8] = b43legacy_phy_read(dev,
B43legacy_PHY_G_CRS);
backup[9] = b43legacy_phy_read(dev, 0x0802);
b43legacy_phy_write(dev, 0x0814,
(b43legacy_phy_read(dev, 0x0814)
| 0x0003));
b43legacy_phy_write(dev, 0x0815,
(b43legacy_phy_read(dev, 0x0815)
& 0xFFFC));
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
(b43legacy_phy_read(dev,
B43legacy_PHY_G_CRS) & 0x7FFF));
b43legacy_phy_write(dev, 0x0802,
(b43legacy_phy_read(dev, 0x0802)
& 0xFFFC));
if (phy->rev > 1) { /* loopback gain enabled */
backup[19] = b43legacy_phy_read(dev, 0x080F);
backup[20] = b43legacy_phy_read(dev, 0x0810);
if (phy->rev >= 3)
b43legacy_phy_write(dev, 0x080F,
0xC020);
else
b43legacy_phy_write(dev, 0x080F,
0x8020);
b43legacy_phy_write(dev, 0x0810, 0x0000);
}
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(0, 1, 1)));
if (phy->rev < 7 ||
!(dev->dev->bus->sprom.r1.boardflags_lo
& B43legacy_BFL_EXTLNA))
b43legacy_phy_write(dev, 0x0811, 0x01B3);
else
b43legacy_phy_write(dev, 0x0811, 0x09B3);
}
}
b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO,
(b43legacy_read16(dev, B43legacy_MMIO_PHY_RADIO)
| 0x8000));
backup[10] = b43legacy_phy_read(dev, 0x0035);
b43legacy_phy_write(dev, 0x0035,
(b43legacy_phy_read(dev, 0x0035) & 0xFF7F));
backup[11] = b43legacy_read16(dev, 0x03E6);
backup[12] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
/* Initialization */
if (phy->analog == 0)
b43legacy_write16(dev, 0x03E6, 0x0122);
else {
if (phy->analog >= 2)
b43legacy_phy_write(dev, 0x0003,
(b43legacy_phy_read(dev, 0x0003)
& 0xFFBF) | 0x0040);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
(b43legacy_read16(dev,
B43legacy_MMIO_CHANNEL_EXT) | 0x2000));
}
ret = b43legacy_radio_calibrationvalue(dev);
if (phy->type == B43legacy_PHYTYPE_B)
b43legacy_radio_write16(dev, 0x0078, 0x0026);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(0, 1, 1)));
b43legacy_phy_write(dev, 0x0015, 0xBFAF);
b43legacy_phy_write(dev, 0x002B, 0x1403);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(0, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xBFA0);
b43legacy_radio_write16(dev, 0x0051,
(b43legacy_radio_read16(dev, 0x0051)
| 0x0004));
if (phy->radio_rev == 8)
b43legacy_radio_write16(dev, 0x0043, 0x001F);
else {
b43legacy_radio_write16(dev, 0x0052, 0x0000);
b43legacy_radio_write16(dev, 0x0043,
(b43legacy_radio_read16(dev, 0x0043)
& 0xFFF0) | 0x0009);
}
b43legacy_phy_write(dev, 0x0058, 0x0000);
for (i = 0; i < 16; i++) {
b43legacy_phy_write(dev, 0x005A, 0x0480);
b43legacy_phy_write(dev, 0x0059, 0xC810);
b43legacy_phy_write(dev, 0x0058, 0x000D);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xAFB0);
udelay(10);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xEFB0);
udelay(10);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 0)));
b43legacy_phy_write(dev, 0x0015, 0xFFF0);
udelay(20);
tmp1 += b43legacy_phy_read(dev, 0x002D);
b43legacy_phy_write(dev, 0x0058, 0x0000);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xAFB0);
}
tmp1++;
tmp1 >>= 9;
udelay(10);
b43legacy_phy_write(dev, 0x0058, 0x0000);
for (i = 0; i < 16; i++) {
b43legacy_radio_write16(dev, 0x0078, (flip_4bit(i) << 1)
| 0x0020);
backup[13] = b43legacy_radio_read16(dev, 0x0078);
udelay(10);
for (j = 0; j < 16; j++) {
b43legacy_phy_write(dev, 0x005A, 0x0D80);
b43legacy_phy_write(dev, 0x0059, 0xC810);
b43legacy_phy_write(dev, 0x0058, 0x000D);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xAFB0);
udelay(10);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xEFB0);
udelay(10);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 0)));
b43legacy_phy_write(dev, 0x0015, 0xFFF0);
udelay(10);
tmp2 += b43legacy_phy_read(dev, 0x002D);
b43legacy_phy_write(dev, 0x0058, 0x0000);
if (phy->gmode)
b43legacy_phy_write(dev, 0x0812,
b43legacy_get_812_value(dev,
LPD(1, 0, 1)));
b43legacy_phy_write(dev, 0x0015, 0xAFB0);
}
tmp2++;
tmp2 >>= 8;
if (tmp1 < tmp2)
break;
}
/* Restore the registers */
b43legacy_phy_write(dev, 0x0015, backup[1]);
b43legacy_radio_write16(dev, 0x0051, backup[14]);
b43legacy_radio_write16(dev, 0x0052, backup[15]);
b43legacy_radio_write16(dev, 0x0043, backup[0]);
b43legacy_phy_write(dev, 0x005A, backup[16]);
b43legacy_phy_write(dev, 0x0059, backup[17]);
b43legacy_phy_write(dev, 0x0058, backup[18]);
b43legacy_write16(dev, 0x03E6, backup[11]);
if (phy->analog != 0)
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[12]);
b43legacy_phy_write(dev, 0x0035, backup[10]);
b43legacy_radio_selectchannel(dev, phy->channel, 1);
if (phy->type == B43legacy_PHYTYPE_B) {
b43legacy_phy_write(dev, 0x0030, backup[2]);
b43legacy_write16(dev, 0x03EC, backup[3]);
} else {
if (phy->gmode) {
b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO,
(b43legacy_read16(dev,
B43legacy_MMIO_PHY_RADIO) & 0x7FFF));
b43legacy_phy_write(dev, 0x0811, backup[4]);
b43legacy_phy_write(dev, 0x0812, backup[5]);
b43legacy_phy_write(dev, 0x0814, backup[6]);
b43legacy_phy_write(dev, 0x0815, backup[7]);
b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
backup[8]);
b43legacy_phy_write(dev, 0x0802, backup[9]);
if (phy->rev > 1) {
b43legacy_phy_write(dev, 0x080F, backup[19]);
b43legacy_phy_write(dev, 0x0810, backup[20]);
}
}
}
if (i >= 15)
ret = backup[13];
return ret;
}
static inline
u16 freq_r3A_value(u16 frequency)
{
u16 value;
if (frequency < 5091)
value = 0x0040;
else if (frequency < 5321)
value = 0x0000;
else if (frequency < 5806)
value = 0x0080;
else
value = 0x0040;
return value;
}
void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev)
{
static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 };
static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A };
u16 tmp = b43legacy_radio_read16(dev, 0x001E);
int i;
int j;
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
if (tmp == (data_high[i] | data_low[j])) {
b43legacy_phy_write(dev, 0x0069, (i - j) << 8 |
0x00C0);
return;
}
}
}
}
int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev,
u8 channel,
int synthetic_pu_workaround)
{
struct b43legacy_phy *phy = &dev->phy;
/* TODO: Check if channel is valid - return -EINVAL if not */
if (synthetic_pu_workaround)
b43legacy_synth_pu_workaround(dev, channel);
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
channel2freq_bg(channel));
if (channel == 14) {
if (dev->dev->bus->sprom.r1.country_code == 5) /* JAPAN) */
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
b43legacy_shm_read32(dev,
B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET)
& ~(1 << 7));
else
b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET,
b43legacy_shm_read32(dev,
B43legacy_SHM_SHARED,
B43legacy_UCODEFLAGS_OFFSET)
| (1 << 7));
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
b43legacy_read16(dev,
B43legacy_MMIO_CHANNEL_EXT) | (1 << 11));
} else
b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
b43legacy_read16(dev,
B43legacy_MMIO_CHANNEL_EXT) & 0xF7BF);
phy->channel = channel;
/*XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states
* that 2000 usecs might suffice. */
msleep(8);
return 0;
}
void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val)
{
u16 tmp;
val <<= 8;
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0022) & 0xFCFF;
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0022, tmp | val);
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x03A8) & 0xFCFF;
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x03A8, tmp | val);
tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0054) & 0xFCFF;
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0054, tmp | val);
}
/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */
static u16 b43legacy_get_txgain_base_band(u16 txpower)
{
u16 ret;
B43legacy_WARN_ON(txpower > 63);
if (txpower >= 54)
ret = 2;
else if (txpower >= 49)
ret = 4;
else if (txpower >= 44)
ret = 5;
else
ret = 6;
return ret;
}
/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */
static u16 b43legacy_get_txgain_freq_power_amp(u16 txpower)
{
u16 ret;
B43legacy_WARN_ON(txpower > 63);
if (txpower >= 32)
ret = 0;
else if (txpower >= 25)
ret = 1;
else if (txpower >= 20)
ret = 2;
else if (txpower >= 12)
ret = 3;
else
ret = 4;
return ret;
}
/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */
static u16 b43legacy_get_txgain_dac(u16 txpower)
{
u16 ret;
B43legacy_WARN_ON(txpower > 63);
if (txpower >= 54)
ret = txpower - 53;
else if (txpower >= 49)
ret = txpower - 42;
else if (txpower >= 44)
ret = txpower - 37;
else if (txpower >= 32)
ret = txpower - 32;
else if (txpower >= 25)
ret = txpower - 20;
else if (txpower >= 20)
ret = txpower - 13;
else if (txpower >= 12)
ret = txpower - 8;
else
ret = txpower;
return ret;
}
void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower)
{
struct b43legacy_phy *phy = &dev->phy;
u16 pamp;
u16 base;
u16 dac;
u16 ilt;
txpower = limit_value(txpower, 0, 63);
pamp = b43legacy_get_txgain_freq_power_amp(txpower);
pamp <<= 5;
pamp &= 0x00E0;
b43legacy_phy_write(dev, 0x0019, pamp);
base = b43legacy_get_txgain_base_band(txpower);
base &= 0x000F;
b43legacy_phy_write(dev, 0x0017, base | 0x0020);
ilt = b43legacy_ilt_read(dev, 0x3001);
ilt &= 0x0007;
dac = b43legacy_get_txgain_dac(txpower);
dac <<= 3;
dac |= ilt;
b43legacy_ilt_write(dev, 0x3001, dac);
phy->txpwr_offset = txpower;
/* TODO: FuncPlaceholder (Adjust BB loft cancel) */
}
void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev,
u16 baseband_attenuation,
u16 radio_attenuation,
u16 txpower)
{
struct b43legacy_phy *phy = &dev->phy;
if (baseband_attenuation == 0xFFFF)
baseband_attenuation = phy->bbatt;
if (radio_attenuation == 0xFFFF)
radio_attenuation = phy->rfatt;
if (txpower == 0xFFFF)
txpower = phy->txctl1;
phy->bbatt = baseband_attenuation;
phy->rfatt = radio_attenuation;
phy->txctl1 = txpower;
B43legacy_WARN_ON(baseband_attenuation > 11);
if (phy->radio_rev < 6)
B43legacy_WARN_ON(radio_attenuation > 9);
else
B43legacy_WARN_ON(radio_attenuation > 31);
B43legacy_WARN_ON(txpower > 7);
b43legacy_phy_set_baseband_attenuation(dev, baseband_attenuation);
b43legacy_radio_write16(dev, 0x0043, radio_attenuation);
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0064,
radio_attenuation);
if (phy->radio_ver == 0x2050)
b43legacy_radio_write16(dev, 0x0052,
(b43legacy_radio_read16(dev, 0x0052)
& ~0x0070) | ((txpower << 4) & 0x0070));
/* FIXME: The spec is very weird and unclear here. */
if (phy->type == B43legacy_PHYTYPE_G)
b43legacy_phy_lo_adjust(dev, 0);
}
u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
if (phy->radio_ver == 0x2050 && phy->radio_rev < 6)
return 0;
return 2;
}
u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
u16 att = 0xFFFF;
switch (phy->radio_ver) {
case 0x2053:
switch (phy->radio_rev) {
case 1:
att = 6;
break;
}
break;
case 0x2050:
switch (phy->radio_rev) {
case 0:
att = 5;
break;
case 1:
if (phy->type == B43legacy_PHYTYPE_G) {
if (is_bcm_board_vendor(dev) &&
dev->dev->bus->boardinfo.type == 0x421 &&
dev->dev->bus->boardinfo.rev >= 30)
att = 3;
else if (is_bcm_board_vendor(dev) &&
dev->dev->bus->boardinfo.type == 0x416)
att = 3;
else
att = 1;
} else {
if (is_bcm_board_vendor(dev) &&
dev->dev->bus->boardinfo.type == 0x421 &&
dev->dev->bus->boardinfo.rev >= 30)
att = 7;
else
att = 6;
}
break;
case 2:
if (phy->type == B43legacy_PHYTYPE_G) {
if (is_bcm_board_vendor(dev) &&
dev->dev->bus->boardinfo.type == 0x421 &&
dev->dev->bus->boardinfo.rev >= 30)
att = 3;
else if (is_bcm_board_vendor(dev) &&
dev->dev->bus->boardinfo.type ==
0x416)
att = 5;
else if (dev->dev->bus->chip_id == 0x4320)
att = 4;
else
att = 3;
} else
att = 6;
break;
case 3:
att = 5;
break;
case 4:
case 5:
att = 1;
break;
case 6:
case 7:
att = 5;
break;
case 8:
att = 0x1A;
break;
case 9:
default:
att = 5;
}
}
if (is_bcm_board_vendor(dev) &&
dev->dev->bus->boardinfo.type == 0x421) {
if (dev->dev->bus->boardinfo.rev < 0x43)
att = 2;
else if (dev->dev->bus->boardinfo.rev < 0x51)
att = 3;
}
if (att == 0xFFFF)
att = 5;
return att;
}
u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
if (phy->radio_ver != 0x2050)
return 0;
if (phy->radio_rev == 1)
return 3;
if (phy->radio_rev < 6)
return 2;
if (phy->radio_rev == 8)
return 1;
return 0;
}
void b43legacy_radio_turn_on(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
int err;
might_sleep();
if (phy->radio_on)
return;
switch (phy->type) {
case B43legacy_PHYTYPE_B:
case B43legacy_PHYTYPE_G:
b43legacy_phy_write(dev, 0x0015, 0x8000);
b43legacy_phy_write(dev, 0x0015, 0xCC00);
b43legacy_phy_write(dev, 0x0015,
(phy->gmode ? 0x00C0 : 0x0000));
err = b43legacy_radio_selectchannel(dev,
B43legacy_RADIO_DEFAULT_CHANNEL_BG, 1);
B43legacy_WARN_ON(err != 0);
break;
default:
B43legacy_BUG_ON(1);
}
phy->radio_on = 1;
b43legacydbg(dev->wl, "Radio turned on\n");
b43legacy_leds_update(dev, 0);
}
void b43legacy_radio_turn_off(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
if (phy->type == B43legacy_PHYTYPE_G && dev->dev->id.revision >= 5) {
b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811)
| 0x008C);
b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812)
& 0xFF73);
} else
b43legacy_phy_write(dev, 0x0015, 0xAA00);
phy->radio_on = 0;
b43legacydbg(dev->wl, "Radio turned off\n");
b43legacy_leds_update(dev, 0);
}
void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
switch (phy->type) {
case B43legacy_PHYTYPE_B:
case B43legacy_PHYTYPE_G:
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0058,
0x7F7F);
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x005a,
0x7F7F);
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0070,
0x7F7F);
b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0072,
0x7F7F);
break;
}
}
/*
Broadcom B43legacy 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.
*/
#ifndef B43legacy_RADIO_H_
#define B43legacy_RADIO_H_
#include "b43legacy.h"
#define B43legacy_RADIO_DEFAULT_CHANNEL_BG 6
/* Force antenna 0. */
#define B43legacy_RADIO_TXANTENNA_0 0
/* Force antenna 1. */
#define B43legacy_RADIO_TXANTENNA_1 1
/* Use the RX antenna, that was selected for the most recently
* received good PLCP header.
*/
#define B43legacy_RADIO_TXANTENNA_LASTPLCP 3
#define B43legacy_RADIO_TXANTENNA_DEFAULT B43legacy_RADIO_TXANTENNA_LASTPLCP
#define B43legacy_RADIO_INTERFMODE_NONE 0
#define B43legacy_RADIO_INTERFMODE_NONWLAN 1
#define B43legacy_RADIO_INTERFMODE_MANUALWLAN 2
#define B43legacy_RADIO_INTERFMODE_AUTOWLAN 3
void b43legacy_radio_lock(struct b43legacy_wldev *dev);
void b43legacy_radio_unlock(struct b43legacy_wldev *dev);
u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset);
void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val);
u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev);
void b43legacy_radio_turn_on(struct b43legacy_wldev *dev);
void b43legacy_radio_turn_off(struct b43legacy_wldev *dev);
int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, u8 channel,
int synthetic_pu_workaround);
void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower);
void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev,
u16 baseband_attenuation, u16 attenuation,
u16 txpower);
u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev);
u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev);
u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev);
void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val);
void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev);
u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel);
u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev);
int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev,
int mode);
void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev);
void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev);
s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset);
void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val);
void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val);
void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev);
void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev);
u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev);
#endif /* B43legacy_RADIO_H_ */
/*
Broadcom B43legacy wireless driver
SYSFS support routines
Copyright (c) 2006 Michael Buesch <mb@bu3sch.de>
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 "sysfs.h"
#include "b43legacy.h"
#include "main.h"
#include "phy.h"
#include "radio.h"
#include <linux/capability.h>
#define GENERIC_FILESIZE 64
static int get_integer(const char *buf, size_t count)
{
char tmp[10 + 1] = { 0 };
int ret = -EINVAL;
if (count == 0)
goto out;
count = min(count, (size_t)10);
memcpy(tmp, buf, count);
ret = simple_strtol(tmp, NULL, 10);
out:
return ret;
}
static int get_boolean(const char *buf, size_t count)
{
if (count != 0) {
if (buf[0] == '1')
return 1;
if (buf[0] == '0')
return 0;
if (count >= 4 && memcmp(buf, "true", 4) == 0)
return 1;
if (count >= 5 && memcmp(buf, "false", 5) == 0)
return 0;
if (count >= 3 && memcmp(buf, "yes", 3) == 0)
return 1;
if (count >= 2 && memcmp(buf, "no", 2) == 0)
return 0;
if (count >= 2 && memcmp(buf, "on", 2) == 0)
return 1;
if (count >= 3 && memcmp(buf, "off", 3) == 0)
return 0;
}
return -EINVAL;
}
static ssize_t b43legacy_attr_interfmode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
ssize_t count = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mutex_lock(&wldev->wl->mutex);
switch (wldev->phy.interfmode) {
case B43legacy_INTERFMODE_NONE:
count = snprintf(buf, PAGE_SIZE, "0 (No Interference"
" Mitigation)\n");
break;
case B43legacy_INTERFMODE_NONWLAN:
count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference"
" Mitigation)\n");
break;
case B43legacy_INTERFMODE_MANUALWLAN:
count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference"
" Mitigation)\n");
break;
default:
B43legacy_WARN_ON(1);
}
mutex_unlock(&wldev->wl->mutex);
return count;
}
static ssize_t b43legacy_attr_interfmode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
unsigned long flags;
int err;
int mode;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mode = get_integer(buf, count);
switch (mode) {
case 0:
mode = B43legacy_INTERFMODE_NONE;
break;
case 1:
mode = B43legacy_INTERFMODE_NONWLAN;
break;
case 2:
mode = B43legacy_INTERFMODE_MANUALWLAN;
break;
case 3:
mode = B43legacy_INTERFMODE_AUTOWLAN;
break;
default:
return -EINVAL;
}
mutex_lock(&wldev->wl->mutex);
spin_lock_irqsave(&wldev->wl->irq_lock, flags);
err = b43legacy_radio_set_interference_mitigation(wldev, mode);
if (err)
b43legacyerr(wldev->wl, "Interference Mitigation not "
"supported by device\n");
mmiowb();
spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
mutex_unlock(&wldev->wl->mutex);
return err ? err : count;
}
static DEVICE_ATTR(interference, 0644,
b43legacy_attr_interfmode_show,
b43legacy_attr_interfmode_store);
static ssize_t b43legacy_attr_preamble_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
ssize_t count;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mutex_lock(&wldev->wl->mutex);
if (wldev->short_preamble)
count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble"
" enabled)\n");
else
count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble"
" disabled)\n");
mutex_unlock(&wldev->wl->mutex);
return count;
}
static ssize_t b43legacy_attr_preamble_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
unsigned long flags;
int value;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
value = get_boolean(buf, count);
if (value < 0)
return value;
mutex_lock(&wldev->wl->mutex);
spin_lock_irqsave(&wldev->wl->irq_lock, flags);
wldev->short_preamble = !!value;
spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
mutex_unlock(&wldev->wl->mutex);
return count;
}
static DEVICE_ATTR(shortpreamble, 0644,
b43legacy_attr_preamble_show,
b43legacy_attr_preamble_store);
int b43legacy_sysfs_register(struct b43legacy_wldev *wldev)
{
struct device *dev = wldev->dev->dev;
int err;
B43legacy_WARN_ON(b43legacy_status(wldev) !=
B43legacy_STAT_INITIALIZED);
err = device_create_file(dev, &dev_attr_interference);
if (err)
goto out;
err = device_create_file(dev, &dev_attr_shortpreamble);
if (err)
goto err_remove_interfmode;
out:
return err;
err_remove_interfmode:
device_remove_file(dev, &dev_attr_interference);
goto out;
}
void b43legacy_sysfs_unregister(struct b43legacy_wldev *wldev)
{
struct device *dev = wldev->dev->dev;
device_remove_file(dev, &dev_attr_shortpreamble);
device_remove_file(dev, &dev_attr_interference);
}
#ifndef B43legacy_SYSFS_H_
#define B43legacy_SYSFS_H_
struct b43legacy_wldev;
int b43legacy_sysfs_register(struct b43legacy_wldev *dev);
void b43legacy_sysfs_unregister(struct b43legacy_wldev *dev);
#endif /* B43legacy_SYSFS_H_ */
/*
Broadcom B43legacy wireless driver
Transmission (TX/RX) related functions.
Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
Copyright (C) 2005, 2006 Michael Buesch <mb@bu3sch.de>
Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
Copyright (C) 2007 Larry Finger <Larry.Finger@lwfinger.net>
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 <net/dst.h>
#include "xmit.h"
#include "phy.h"
#include "dma.h"
#include "pio.h"
/* Extract the bitrate out of a CCK PLCP header. */
static u8 b43legacy_plcp_get_bitrate_cck(struct b43legacy_plcp_hdr6 *plcp)
{
switch (plcp->raw[0]) {
case 0x0A:
return B43legacy_CCK_RATE_1MB;
case 0x14:
return B43legacy_CCK_RATE_2MB;
case 0x37:
return B43legacy_CCK_RATE_5MB;
case 0x6E:
return B43legacy_CCK_RATE_11MB;
}
B43legacy_BUG_ON(1);
return 0;
}
/* Extract the bitrate out of an OFDM PLCP header. */
static u8 b43legacy_plcp_get_bitrate_ofdm(struct b43legacy_plcp_hdr6 *plcp)
{
switch (plcp->raw[0] & 0xF) {
case 0xB:
return B43legacy_OFDM_RATE_6MB;
case 0xF:
return B43legacy_OFDM_RATE_9MB;
case 0xA:
return B43legacy_OFDM_RATE_12MB;
case 0xE:
return B43legacy_OFDM_RATE_18MB;
case 0x9:
return B43legacy_OFDM_RATE_24MB;
case 0xD:
return B43legacy_OFDM_RATE_36MB;
case 0x8:
return B43legacy_OFDM_RATE_48MB;
case 0xC:
return B43legacy_OFDM_RATE_54MB;
}
B43legacy_BUG_ON(1);
return 0;
}
u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate)
{
switch (bitrate) {
case B43legacy_CCK_RATE_1MB:
return 0x0A;
case B43legacy_CCK_RATE_2MB:
return 0x14;
case B43legacy_CCK_RATE_5MB:
return 0x37;
case B43legacy_CCK_RATE_11MB:
return 0x6E;
}
B43legacy_BUG_ON(1);
return 0;
}
u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate)
{
switch (bitrate) {
case B43legacy_OFDM_RATE_6MB:
return 0xB;
case B43legacy_OFDM_RATE_9MB:
return 0xF;
case B43legacy_OFDM_RATE_12MB:
return 0xA;
case B43legacy_OFDM_RATE_18MB:
return 0xE;
case B43legacy_OFDM_RATE_24MB:
return 0x9;
case B43legacy_OFDM_RATE_36MB:
return 0xD;
case B43legacy_OFDM_RATE_48MB:
return 0x8;
case B43legacy_OFDM_RATE_54MB:
return 0xC;
}
B43legacy_BUG_ON(1);
return 0;
}
void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp,
const u16 octets, const u8 bitrate)
{
__le32 *data = &(plcp->data);
__u8 *raw = plcp->raw;
if (b43legacy_is_ofdm_rate(bitrate)) {
u16 d;
d = b43legacy_plcp_get_ratecode_ofdm(bitrate);
B43legacy_WARN_ON(octets & 0xF000);
d |= (octets << 5);
*data = cpu_to_le32(d);
} else {
u32 plen;
plen = octets * 16 / bitrate;
if ((octets * 16 % bitrate) > 0) {
plen++;
if ((bitrate == B43legacy_CCK_RATE_11MB)
&& ((octets * 8 % 11) < 4))
raw[1] = 0x84;
else
raw[1] = 0x04;
} else
raw[1] = 0x04;
*data |= cpu_to_le32(plen << 16);
raw[0] = b43legacy_plcp_get_ratecode_cck(bitrate);
}
}
static u8 b43legacy_calc_fallback_rate(u8 bitrate)
{
switch (bitrate) {
case B43legacy_CCK_RATE_1MB:
return B43legacy_CCK_RATE_1MB;
case B43legacy_CCK_RATE_2MB:
return B43legacy_CCK_RATE_1MB;
case B43legacy_CCK_RATE_5MB:
return B43legacy_CCK_RATE_2MB;
case B43legacy_CCK_RATE_11MB:
return B43legacy_CCK_RATE_5MB;
case B43legacy_OFDM_RATE_6MB:
return B43legacy_CCK_RATE_5MB;
case B43legacy_OFDM_RATE_9MB:
return B43legacy_OFDM_RATE_6MB;
case B43legacy_OFDM_RATE_12MB:
return B43legacy_OFDM_RATE_9MB;
case B43legacy_OFDM_RATE_18MB:
return B43legacy_OFDM_RATE_12MB;
case B43legacy_OFDM_RATE_24MB:
return B43legacy_OFDM_RATE_18MB;
case B43legacy_OFDM_RATE_36MB:
return B43legacy_OFDM_RATE_24MB;
case B43legacy_OFDM_RATE_48MB:
return B43legacy_OFDM_RATE_36MB;
case B43legacy_OFDM_RATE_54MB:
return B43legacy_OFDM_RATE_48MB;
}
B43legacy_BUG_ON(1);
return 0;
}
static void generate_txhdr_fw3(struct b43legacy_wldev *dev,
struct b43legacy_txhdr_fw3 *txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
const struct ieee80211_tx_control *txctl,
u16 cookie)
{
const struct ieee80211_hdr *wlhdr;
int use_encryption = (!(txctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT));
u16 fctl;
u8 rate;
u8 rate_fb;
int rate_ofdm;
int rate_fb_ofdm;
unsigned int plcp_fragment_len;
u32 mac_ctl = 0;
u16 phy_ctl = 0;
wlhdr = (const struct ieee80211_hdr *)fragment_data;
fctl = le16_to_cpu(wlhdr->frame_control);
memset(txhdr, 0, sizeof(*txhdr));
rate = txctl->tx_rate;
rate_ofdm = b43legacy_is_ofdm_rate(rate);
rate_fb = (txctl->alt_retry_rate == -1) ? rate : txctl->alt_retry_rate;
rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb);
txhdr->mac_frame_ctl = wlhdr->frame_control;
memcpy(txhdr->tx_receiver, wlhdr->addr1, 6);
/* Calculate duration for fallback rate */
if ((rate_fb == rate) ||
(wlhdr->duration_id & cpu_to_le16(0x8000)) ||
(wlhdr->duration_id == cpu_to_le16(0))) {
/* If the fallback rate equals the normal rate or the
* dur_id field contains an AID, CFP magic or 0,
* use the original dur_id field. */
txhdr->dur_fb = wlhdr->duration_id;
} else {
int fbrate_base100kbps = B43legacy_RATE_TO_100KBPS(rate_fb);
txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw,
dev->wl->if_id,
fragment_len,
fbrate_base100kbps);
}
plcp_fragment_len = fragment_len + FCS_LEN;
if (use_encryption) {
u8 key_idx = (u16)(txctl->key_idx);
struct b43legacy_key *key;
int wlhdr_len;
size_t iv_len;
B43legacy_WARN_ON(key_idx >= dev->max_nr_keys);
key = &(dev->key[key_idx]);
if (key->enabled) {
/* Hardware appends ICV. */
plcp_fragment_len += txctl->icv_len;
key_idx = b43legacy_kidx_to_fw(dev, key_idx);
mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) &
B43legacy_TX4_MAC_KEYIDX;
mac_ctl |= (key->algorithm <<
B43legacy_TX4_MAC_KEYALG_SHIFT) &
B43legacy_TX4_MAC_KEYALG;
wlhdr_len = ieee80211_get_hdrlen(fctl);
iv_len = min((size_t)txctl->iv_len,
ARRAY_SIZE(txhdr->iv));
memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len);
}
}
b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *)
(&txhdr->plcp), plcp_fragment_len,
rate);
b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *)
(&txhdr->plcp_fb), plcp_fragment_len,
rate_fb);
/* PHY TX Control word */
if (rate_ofdm)
phy_ctl |= B43legacy_TX4_PHY_OFDM;
if (dev->short_preamble)
phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL;
switch (txctl->antenna_sel_tx) {
case 0:
phy_ctl |= B43legacy_TX4_PHY_ANTLAST;
break;
case 1:
phy_ctl |= B43legacy_TX4_PHY_ANT0;
break;
case 2:
phy_ctl |= B43legacy_TX4_PHY_ANT1;
break;
default:
B43legacy_BUG_ON(1);
}
/* MAC control */
if (!(txctl->flags & IEEE80211_TXCTL_NO_ACK))
mac_ctl |= B43legacy_TX4_MAC_ACK;
if (!(((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) &&
((fctl & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)))
mac_ctl |= B43legacy_TX4_MAC_HWSEQ;
if (txctl->flags & IEEE80211_TXCTL_FIRST_FRAGMENT)
mac_ctl |= B43legacy_TX4_MAC_STMSDU;
if (rate_fb_ofdm)
mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM;
/* Generate the RTS or CTS-to-self frame */
if ((txctl->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
(txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
unsigned int len;
struct ieee80211_hdr *hdr;
int rts_rate;
int rts_rate_fb;
int rts_rate_ofdm;
int rts_rate_fb_ofdm;
rts_rate = txctl->rts_cts_rate;
rts_rate_ofdm = b43legacy_is_ofdm_rate(rts_rate);
rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate);
rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb);
if (rts_rate_fb_ofdm)
mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM;
if (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
ieee80211_ctstoself_get(dev->wl->hw,
dev->wl->if_id,
fragment_data,
fragment_len, txctl,
(struct ieee80211_cts *)
(txhdr->rts_frame));
mac_ctl |= B43legacy_TX4_MAC_SENDCTS;
len = sizeof(struct ieee80211_cts);
} else {
ieee80211_rts_get(dev->wl->hw,
dev->wl->if_id,
fragment_data, fragment_len, txctl,
(struct ieee80211_rts *)
(txhdr->rts_frame));
mac_ctl |= B43legacy_TX4_MAC_SENDRTS;
len = sizeof(struct ieee80211_rts);
}
len += FCS_LEN;
b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *)
(&txhdr->rts_plcp),
len, rts_rate);
b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *)
(&txhdr->rts_plcp_fb),
len, rts_rate_fb);
hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame);
txhdr->rts_dur_fb = hdr->duration_id;
mac_ctl |= B43legacy_TX4_MAC_LONGFRAME;
}
/* Magic cookie */
txhdr->cookie = cpu_to_le16(cookie);
/* Apply the bitfields */
txhdr->mac_ctl = cpu_to_le32(mac_ctl);
txhdr->phy_ctl = cpu_to_le16(phy_ctl);
}
void b43legacy_generate_txhdr(struct b43legacy_wldev *dev,
u8 *txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
const struct ieee80211_tx_control *txctl,
u16 cookie)
{
generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr,
fragment_data, fragment_len,
txctl, cookie);
}
static s8 b43legacy_rssi_postprocess(struct b43legacy_wldev *dev,
u8 in_rssi, int ofdm,
int adjust_2053, int adjust_2050)
{
struct b43legacy_phy *phy = &dev->phy;
s32 tmp;
switch (phy->radio_ver) {
case 0x2050:
if (ofdm) {
tmp = in_rssi;
if (tmp > 127)
tmp -= 256;
tmp *= 73;
tmp /= 64;
if (adjust_2050)
tmp += 25;
else
tmp -= 3;
} else {
if (dev->dev->bus->sprom.r1.boardflags_lo
& B43legacy_BFL_RSSI) {
if (in_rssi > 63)
in_rssi = 63;
tmp = phy->nrssi_lt[in_rssi];
tmp = 31 - tmp;
tmp *= -131;
tmp /= 128;
tmp -= 57;
} else {
tmp = in_rssi;
tmp = 31 - tmp;
tmp *= -149;
tmp /= 128;
tmp -= 68;
}
if (phy->type == B43legacy_PHYTYPE_G &&
adjust_2050)
tmp += 25;
}
break;
case 0x2060:
if (in_rssi > 127)
tmp = in_rssi - 256;
else
tmp = in_rssi;
break;
default:
tmp = in_rssi;
tmp -= 11;
tmp *= 103;
tmp /= 64;
if (adjust_2053)
tmp -= 109;
else
tmp -= 83;
}
return (s8)tmp;
}
void b43legacy_rx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
const void *_rxhdr)
{
struct ieee80211_rx_status status;
struct b43legacy_plcp_hdr6 *plcp;
struct ieee80211_hdr *wlhdr;
const struct b43legacy_rxhdr_fw3 *rxhdr = _rxhdr;
u16 fctl;
u16 phystat0;
u16 phystat3;
u16 chanstat;
u16 mactime;
u32 macstat;
u16 chanid;
u8 jssi;
int padding;
memset(&status, 0, sizeof(status));
/* Get metadata about the frame from the header. */
phystat0 = le16_to_cpu(rxhdr->phy_status0);
phystat3 = le16_to_cpu(rxhdr->phy_status3);
jssi = rxhdr->jssi;
macstat = le16_to_cpu(rxhdr->mac_status);
mactime = le16_to_cpu(rxhdr->mac_time);
chanstat = le16_to_cpu(rxhdr->channel);
if (macstat & B43legacy_RX_MAC_FCSERR)
dev->wl->ieee_stats.dot11FCSErrorCount++;
/* Skip PLCP and padding */
padding = (macstat & B43legacy_RX_MAC_PADDING) ? 2 : 0;
if (unlikely(skb->len < (sizeof(struct b43legacy_plcp_hdr6) +
padding))) {
b43legacydbg(dev->wl, "RX: Packet size underrun (1)\n");
goto drop;
}
plcp = (struct b43legacy_plcp_hdr6 *)(skb->data + padding);
skb_pull(skb, sizeof(struct b43legacy_plcp_hdr6) + padding);
/* The skb contains the Wireless Header + payload data now */
if (unlikely(skb->len < (2+2+6/*minimum hdr*/ + FCS_LEN))) {
b43legacydbg(dev->wl, "RX: Packet size underrun (2)\n");
goto drop;
}
wlhdr = (struct ieee80211_hdr *)(skb->data);
fctl = le16_to_cpu(wlhdr->frame_control);
if ((macstat & B43legacy_RX_MAC_DEC) &&
!(macstat & B43legacy_RX_MAC_DECERR)) {
unsigned int keyidx;
int wlhdr_len;
int iv_len;
int icv_len;
keyidx = ((macstat & B43legacy_RX_MAC_KEYIDX)
>> B43legacy_RX_MAC_KEYIDX_SHIFT);
/* We must adjust the key index here. We want the "physical"
* key index, but the ucode passed it slightly different.
*/
keyidx = b43legacy_kidx_to_raw(dev, keyidx);
B43legacy_WARN_ON(keyidx >= dev->max_nr_keys);
if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) {
/* Remove PROTECTED flag to mark it as decrypted. */
B43legacy_WARN_ON(!(fctl & IEEE80211_FCTL_PROTECTED));
fctl &= ~IEEE80211_FCTL_PROTECTED;
wlhdr->frame_control = cpu_to_le16(fctl);
wlhdr_len = ieee80211_get_hdrlen(fctl);
if (unlikely(skb->len < (wlhdr_len + 3))) {
b43legacydbg(dev->wl, "RX: Packet size"
" underrun3\n");
goto drop;
}
if (skb->data[wlhdr_len + 3] & (1 << 5)) {
/* The Ext-IV Bit is set in the "KeyID"
* octet of the IV.
*/
iv_len = 8;
icv_len = 8;
} else {
iv_len = 4;
icv_len = 4;
}
if (unlikely(skb->len < (wlhdr_len + iv_len +
icv_len))) {
b43legacydbg(dev->wl, "RX: Packet size"
" underrun4\n");
goto drop;
}
/* Remove the IV */
memmove(skb->data + iv_len, skb->data, wlhdr_len);
skb_pull(skb, iv_len);
/* Remove the ICV */
skb_trim(skb, skb->len - icv_len);
status.flag |= RX_FLAG_DECRYPTED;
}
}
status.ssi = b43legacy_rssi_postprocess(dev, jssi,
(phystat0 & B43legacy_RX_PHYST0_OFDM),
(phystat0 & B43legacy_RX_PHYST0_GAINCTL),
(phystat3 & B43legacy_RX_PHYST3_TRSTATE));
status.noise = dev->stats.link_noise;
status.signal = (jssi * 100) / B43legacy_RX_MAX_SSI;
if (phystat0 & B43legacy_RX_PHYST0_OFDM)
status.rate = b43legacy_plcp_get_bitrate_ofdm(plcp);
else
status.rate = b43legacy_plcp_get_bitrate_cck(plcp);
status.antenna = !!(phystat0 & B43legacy_RX_PHYST0_ANT);
status.mactime = mactime;
chanid = (chanstat & B43legacy_RX_CHAN_ID) >>
B43legacy_RX_CHAN_ID_SHIFT;
switch (chanstat & B43legacy_RX_CHAN_PHYTYPE) {
case B43legacy_PHYTYPE_B:
status.phymode = MODE_IEEE80211B;
status.freq = chanid + 2400;
status.channel = b43legacy_freq_to_channel_bg(chanid + 2400);
break;
case B43legacy_PHYTYPE_G:
status.phymode = MODE_IEEE80211G;
status.freq = chanid + 2400;
status.channel = b43legacy_freq_to_channel_bg(chanid + 2400);
break;
default:
b43legacywarn(dev->wl, "Unexpected value for chanstat (0x%X)\n",
chanstat);
}
dev->stats.last_rx = jiffies;
ieee80211_rx_irqsafe(dev->wl->hw, skb, &status);
return;
drop:
b43legacydbg(dev->wl, "RX: Packet dropped\n");
dev_kfree_skb_any(skb);
}
void b43legacy_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status)
{
b43legacy_debugfs_log_txstat(dev, status);
if (status->intermediate)
return;
if (status->for_ampdu)
return;
if (!status->acked)
dev->wl->ieee_stats.dot11ACKFailureCount++;
if (status->rts_count) {
if (status->rts_count == 0xF) /* FIXME */
dev->wl->ieee_stats.dot11RTSFailureCount++;
else
dev->wl->ieee_stats.dot11RTSSuccessCount++;
}
if (b43legacy_using_pio(dev))
b43legacy_pio_handle_txstatus(dev, status);
else
b43legacy_dma_handle_txstatus(dev, status);
}
/* Handle TX status report as received through DMA/PIO queues */
void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev,
const struct b43legacy_hwtxstatus *hw)
{
struct b43legacy_txstatus status;
u8 tmp;
status.cookie = le16_to_cpu(hw->cookie);
status.seq = le16_to_cpu(hw->seq);
status.phy_stat = hw->phy_stat;
tmp = hw->count;
status.frame_count = (tmp >> 4);
status.rts_count = (tmp & 0x0F);
tmp = hw->flags;
status.supp_reason = ((tmp & 0x1C) >> 2);
status.pm_indicated = !!(tmp & 0x80);
status.intermediate = !!(tmp & 0x40);
status.for_ampdu = !!(tmp & 0x20);
status.acked = !!(tmp & 0x02);
b43legacy_handle_txstatus(dev, &status);
}
/* Stop any TX operation on the device (suspend the hardware queues) */
void b43legacy_tx_suspend(struct b43legacy_wldev *dev)
{
if (b43legacy_using_pio(dev))
b43legacy_pio_freeze_txqueues(dev);
else
b43legacy_dma_tx_suspend(dev);
}
/* Resume any TX operation on the device (resume the hardware queues) */
void b43legacy_tx_resume(struct b43legacy_wldev *dev)
{
if (b43legacy_using_pio(dev))
b43legacy_pio_thaw_txqueues(dev);
else
b43legacy_dma_tx_resume(dev);
}
/* Initialize the QoS parameters */
void b43legacy_qos_init(struct b43legacy_wldev *dev)
{
/* FIXME: This function must probably be called from the mac80211
* config callback. */
return;
b43legacy_hf_write(dev, b43legacy_hf_read(dev) | B43legacy_HF_EDCF);
/* FIXME kill magic */
b43legacy_write16(dev, 0x688,
b43legacy_read16(dev, 0x688) | 0x4);
/*TODO: We might need some stack support here to get the values. */
}
#ifndef B43legacy_XMIT_H_
#define B43legacy_XMIT_H_
#include "main.h"
#define _b43legacy_declare_plcp_hdr(size) \
struct b43legacy_plcp_hdr##size { \
union { \
__le32 data; \
__u8 raw[size]; \
} __attribute__((__packed__)); \
} __attribute__((__packed__))
/* struct b43legacy_plcp_hdr4 */
_b43legacy_declare_plcp_hdr(4);
/* struct b43legacy_plcp_hdr6 */
_b43legacy_declare_plcp_hdr(6);
#undef _b43legacy_declare_plcp_hdr
/* TX header for v3 firmware */
struct b43legacy_txhdr_fw3 {
__le32 mac_ctl; /* MAC TX control */
__le16 mac_frame_ctl; /* Copy of the FrameControl */
__le16 tx_fes_time_norm; /* TX FES Time Normal */
__le16 phy_ctl; /* PHY TX control */
__u8 iv[16]; /* Encryption IV */
__u8 tx_receiver[6]; /* TX Frame Receiver address */
__le16 tx_fes_time_fb; /* TX FES Time Fallback */
struct b43legacy_plcp_hdr4 rts_plcp_fb; /* RTS fallback PLCP */
__le16 rts_dur_fb; /* RTS fallback duration */
struct b43legacy_plcp_hdr4 plcp_fb; /* Fallback PLCP */
__le16 dur_fb; /* Fallback duration */
PAD_BYTES(2);
__le16 cookie;
__le16 unknown_scb_stuff;
struct b43legacy_plcp_hdr6 rts_plcp; /* RTS PLCP */
__u8 rts_frame[18]; /* The RTS frame (if used) */
struct b43legacy_plcp_hdr6 plcp;
} __attribute__((__packed__));
/* MAC TX control */
#define B43legacy_TX4_MAC_KEYIDX 0x0FF00000 /* Security key index */
#define B43legacy_TX4_MAC_KEYIDX_SHIFT 20
#define B43legacy_TX4_MAC_KEYALG 0x00070000 /* Security key algorithm */
#define B43legacy_TX4_MAC_KEYALG_SHIFT 16
#define B43legacy_TX4_MAC_LIFETIME 0x00001000
#define B43legacy_TX4_MAC_FRAMEBURST 0x00000800
#define B43legacy_TX4_MAC_SENDCTS 0x00000400
#define B43legacy_TX4_MAC_AMPDU 0x00000300
#define B43legacy_TX4_MAC_AMPDU_SHIFT 8
#define B43legacy_TX4_MAC_CTSFALLBACKOFDM 0x00000200
#define B43legacy_TX4_MAC_FALLBACKOFDM 0x00000100
#define B43legacy_TX4_MAC_5GHZ 0x00000080
#define B43legacy_TX4_MAC_IGNPMQ 0x00000020
#define B43legacy_TX4_MAC_HWSEQ 0x00000010 /* Use Hardware Seq No */
#define B43legacy_TX4_MAC_STMSDU 0x00000008 /* Start MSDU */
#define B43legacy_TX4_MAC_SENDRTS 0x00000004
#define B43legacy_TX4_MAC_LONGFRAME 0x00000002
#define B43legacy_TX4_MAC_ACK 0x00000001
/* Extra Frame Types */
#define B43legacy_TX4_EFT_FBOFDM 0x0001 /* Data frame fb rate type */
#define B43legacy_TX4_EFT_RTSOFDM 0x0004 /* RTS/CTS rate type */
#define B43legacy_TX4_EFT_RTSFBOFDM 0x0010 /* RTS/CTS fallback rate type */
/* PHY TX control word */
#define B43legacy_TX4_PHY_OFDM 0x0001 /* Data frame rate type */
#define B43legacy_TX4_PHY_SHORTPRMBL 0x0010 /* Use short preamble */
#define B43legacy_TX4_PHY_ANT 0x03C0 /* Antenna selection */
#define B43legacy_TX4_PHY_ANT0 0x0000 /* Use antenna 0 */
#define B43legacy_TX4_PHY_ANT1 0x0100 /* Use antenna 1 */
#define B43legacy_TX4_PHY_ANTLAST 0x0300 /* Use last used antenna */
void b43legacy_generate_txhdr(struct b43legacy_wldev *dev,
u8 *txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
const struct ieee80211_tx_control *txctl,
u16 cookie);
/* Transmit Status */
struct b43legacy_txstatus {
u16 cookie; /* The cookie from the txhdr */
u16 seq; /* Sequence number */
u8 phy_stat; /* PHY TX status */
u8 frame_count; /* Frame transmit count */
u8 rts_count; /* RTS transmit count */
u8 supp_reason; /* Suppression reason */
/* flags */
u8 pm_indicated;/* PM mode indicated to AP */
u8 intermediate;/* Intermediate status notification */
u8 for_ampdu; /* Status is for an AMPDU (afterburner) */
u8 acked; /* Wireless ACK received */
};
/* txstatus supp_reason values */
enum {
B43legacy_TXST_SUPP_NONE, /* Not suppressed */
B43legacy_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */
B43legacy_TXST_SUPP_FLUSH, /* Suppressed due to flush request */
B43legacy_TXST_SUPP_PREV, /* Previous fragment failed */
B43legacy_TXST_SUPP_CHAN, /* Channel mismatch */
B43legacy_TXST_SUPP_LIFE, /* Lifetime expired */
B43legacy_TXST_SUPP_UNDER, /* Buffer underflow */
B43legacy_TXST_SUPP_ABNACK, /* Afterburner NACK */
};
/* Transmit Status as received through DMA/PIO on old chips */
struct b43legacy_hwtxstatus {
PAD_BYTES(4);
__le16 cookie;
u8 flags;
u8 count;
PAD_BYTES(2);
__le16 seq;
u8 phy_stat;
PAD_BYTES(1);
} __attribute__((__packed__));
/* Receive header for v3 firmware. */
struct b43legacy_rxhdr_fw3 {
__le16 frame_len; /* Frame length */
PAD_BYTES(2);
__le16 phy_status0; /* PHY RX Status 0 */
__u8 jssi; /* PHY RX Status 1: JSSI */
__u8 sig_qual; /* PHY RX Status 1: Signal Quality */
PAD_BYTES(2); /* PHY RX Status 2 */
__le16 phy_status3; /* PHY RX Status 3 */
__le16 mac_status; /* MAC RX status */
__le16 mac_time;
__le16 channel;
} __attribute__((__packed__));
/* PHY RX Status 0 */
#define B43legacy_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */
#define B43legacy_RX_PHYST0_PLCPHCF 0x0200
#define B43legacy_RX_PHYST0_PLCPFV 0x0100
#define B43legacy_RX_PHYST0_SHORTPRMBL 0x0080 /* Recvd with Short Preamble */
#define B43legacy_RX_PHYST0_LCRS 0x0040
#define B43legacy_RX_PHYST0_ANT 0x0020 /* Antenna */
#define B43legacy_RX_PHYST0_UNSRATE 0x0010
#define B43legacy_RX_PHYST0_CLIP 0x000C
#define B43legacy_RX_PHYST0_CLIP_SHIFT 2
#define B43legacy_RX_PHYST0_FTYPE 0x0003 /* Frame type */
#define B43legacy_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */
#define B43legacy_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */
#define B43legacy_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */
#define B43legacy_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */
/* PHY RX Status 2 */
#define B43legacy_RX_PHYST2_LNAG 0xC000 /* LNA Gain */
#define B43legacy_RX_PHYST2_LNAG_SHIFT 14
#define B43legacy_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */
#define B43legacy_RX_PHYST2_PNAG_SHIFT 10
#define B43legacy_RX_PHYST2_FOFF 0x03FF /* F offset */
/* PHY RX Status 3 */
#define B43legacy_RX_PHYST3_DIGG 0x1800 /* DIG Gain */
#define B43legacy_RX_PHYST3_DIGG_SHIFT 11
#define B43legacy_RX_PHYST3_TRSTATE 0x0400 /* TR state */
/* MAC RX Status */
#define B43legacy_RX_MAC_BEACONSENT 0x00008000 /* Beacon send flag */
#define B43legacy_RX_MAC_KEYIDX 0x000007E0 /* Key index */
#define B43legacy_RX_MAC_KEYIDX_SHIFT 5
#define B43legacy_RX_MAC_DECERR 0x00000010 /* Decrypt error */
#define B43legacy_RX_MAC_DEC 0x00000008 /* Decryption attempted */
#define B43legacy_RX_MAC_PADDING 0x00000004 /* Pad bytes present */
#define B43legacy_RX_MAC_RESP 0x00000002 /* Response frame xmitted */
#define B43legacy_RX_MAC_FCSERR 0x00000001 /* FCS error */
/* RX channel */
#define B43legacy_RX_CHAN_GAIN 0xFC00 /* Gain */
#define B43legacy_RX_CHAN_GAIN_SHIFT 10
#define B43legacy_RX_CHAN_ID 0x03FC /* Channel ID */
#define B43legacy_RX_CHAN_ID_SHIFT 2
#define B43legacy_RX_CHAN_PHYTYPE 0x0003 /* PHY type */
u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate);
u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate);
void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp,
const u16 octets, const u8 bitrate);
void b43legacy_rx(struct b43legacy_wldev *dev,
struct sk_buff *skb,
const void *_rxhdr);
void b43legacy_handle_txstatus(struct b43legacy_wldev *dev,
const struct b43legacy_txstatus *status);
void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev,
const struct b43legacy_hwtxstatus *hw);
void b43legacy_tx_suspend(struct b43legacy_wldev *dev);
void b43legacy_tx_resume(struct b43legacy_wldev *dev);
#define B43legacy_NR_QOSPARMS 22
enum {
B43legacy_QOSPARM_TXOP = 0,
B43legacy_QOSPARM_CWMIN,
B43legacy_QOSPARM_CWMAX,
B43legacy_QOSPARM_CWCUR,
B43legacy_QOSPARM_AIFS,
B43legacy_QOSPARM_BSLOTS,
B43legacy_QOSPARM_REGGAP,
B43legacy_QOSPARM_STATUS,
};
void b43legacy_qos_init(struct b43legacy_wldev *dev);
/* Helper functions for converting the key-table index from "firmware-format"
* to "raw-format" and back. The firmware API changed for this at some revision.
* We need to account for that here. */
static inline
int b43legacy_new_kidx_api(struct b43legacy_wldev *dev)
{
/* FIXME: Not sure the change was at rev 351 */
return (dev->fw.rev >= 351);
}
static inline
u8 b43legacy_kidx_to_fw(struct b43legacy_wldev *dev, u8 raw_kidx)
{
u8 firmware_kidx;
if (b43legacy_new_kidx_api(dev))
firmware_kidx = raw_kidx;
else {
if (raw_kidx >= 4) /* Is per STA key? */
firmware_kidx = raw_kidx - 4;
else
firmware_kidx = raw_kidx; /* TX default key */
}
return firmware_kidx;
}
static inline
u8 b43legacy_kidx_to_raw(struct b43legacy_wldev *dev, u8 firmware_kidx)
{
u8 raw_kidx;
if (b43legacy_new_kidx_api(dev))
raw_kidx = firmware_kidx;
else
/* RX default keys or per STA keys */
raw_kidx = firmware_kidx + 4;
return raw_kidx;
}
#endif /* B43legacy_XMIT_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment