Commit 2079fcdc authored by Adrian Bunk's avatar Adrian Bunk Committed by John W. Linville

the scheduled bcm43xx removal

Signed-off-by: default avatarAdrian Bunk <bunk@kernel.org>
Cc: <Larry.Finger@lwfinger.net>
Cc: <stefano.brivio@polimi.it>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent affe0a02
......@@ -230,15 +230,6 @@ Who: Jean Delvare <khali@linux-fr.org>
---------------------------
What: bcm43xx wireless network driver
When: 2.6.26
Files: drivers/net/wireless/bcm43xx
Why: This driver's functionality has been replaced by the
mac80211-based b43 and b43legacy drivers.
Who: John W. Linville <linville@tuxdriver.com>
---------------------------
What: ieee80211 softmac wireless networking component
When: 2.6.26 (or after removal of bcm43xx and port of zd1211rw to mac80211)
Files: net/ieee80211/softmac
......
......@@ -80,7 +80,7 @@ once you enable the radio, will depend on your hardware and driver combination.
e.g. With the BCM4318 on the Acer Aspire 5020 series:
ndiswrapper: Light blinks on when transmitting
bcm43xx/b43: Solid light, blinks off when transmitting
b43: Solid light, blinks off when transmitting
Wireless radio control is unconditionally enabled - all Acer laptops that support
acer-wmi come with built-in wireless. However, should you feel so inclined to
......
BCM43xx Linux Driver Project
============================
Introduction
------------
Many of the wireless devices found in modern notebook computers are
based on the wireless chips produced by Broadcom. These devices have
been a problem for Linux users as there is no open-source driver
available. In addition, Broadcom has not released specifications
for the device, and driver availability has been limited to the
binary-only form used in the GPL versions of AP hardware such as the
Linksys WRT54G, and the Windows and OS X drivers. Before this project
began, the only way to use these devices were to use the Windows or
OS X drivers with either the Linuxant or ndiswrapper modules. There
is a strong penalty if this method is used as loading the binary-only
module "taints" the kernel, and no kernel developer will help diagnose
any kernel problems.
Development
-----------
This driver has been developed using
a clean-room technique that is described at
http://bcm-specs.sipsolutions.net/ReverseEngineeringProcess. For legal
reasons, none of the clean-room crew works on the on the Linux driver,
and none of the Linux developers sees anything but the specifications,
which are the ultimate product of the reverse-engineering group.
Software
--------
Since the release of the 2.6.17 kernel, the bcm43xx driver has been
distributed with the kernel source, and is prebuilt in most, if not
all, distributions. There is, however, additional software that is
required. The firmware used by the chip is the intellectual property
of Broadcom and they have not given the bcm43xx team redistribution
rights to this firmware. Since we cannot legally redistribute
the firmware we cannot include it with the driver. Furthermore, it
cannot be placed in the downloadable archives of any distributing
organization; therefore, the user is responsible for obtaining the
firmware and placing it in the appropriate location so that the driver
can find it when initializing.
To help with this process, the bcm43xx developers provide a separate
program named bcm43xx-fwcutter to "cut" the firmware out of a
Windows or OS X driver and write the extracted files to the proper
location. This program is usually provided with the distribution;
however, it may be downloaded from
http://developer.berlios.de/project/showfiles.php?group_id=4547
The firmware is available in two versions. V3 firmware is used with
the in-kernel bcm43xx driver that uses a software MAC layer called
SoftMAC, and will have a microcode revision of 0x127 or smaller. The
V4 firmware is used by an out-of-kernel driver employing a variation of
the Devicescape MAC layer known as d80211. Once bcm43xx-d80211 reaches
a satisfactory level of development, it will replace bcm43xx-softmac
in the kernel as it is much more flexible and powerful.
A source for the latest V3 firmware is
http://downloads.openwrt.org/sources/wl_apsta-3.130.20.0.o
Once this file is downloaded, the command
'bcm43xx-fwcutter -w <dir> <filename>'
will extract the microcode and write it to directory
<dir>. The correct directory will depend on your distribution;
however, most use '/lib/firmware'. Once this step is completed,
the bcm3xx driver should load when the system is booted. To see
any messages relating to the driver, issue the command 'dmesg |
grep bcm43xx' from a terminal window. If there are any problems,
please send that output to Bcm43xx-dev@lists.berlios.de.
Although the driver has been in-kernel since 2.6.17, the earliest
version is quite limited in its capability. Patches that include
all features of later versions are available for the stable kernel
versions from 2.6.18. These will be needed if you use a BCM4318,
or a PCI Express version (BCM4311 and BCM4312). In addition, if you
have an early BCM4306 and more than 1 GB RAM, your kernel will need
to be patched. These patches, which are being updated regularly,
are available at ftp://lwfinger.dynalias.org/patches. Look for
combined_2.6.YY.patch. Of course you will need kernel source downloaded
from kernel.org, or the source from your distribution.
If you build your own kernel, please enable CONFIG_BCM43XX_DEBUG
and CONFIG_IEEE80211_SOFTMAC_DEBUG. The log information provided is
essential for solving any problems.
......@@ -825,15 +825,6 @@ L: linux-wireless@vger.kernel.org
W: http://linuxwireless.org/en/users/Drivers/b43
S: Maintained
BCM43XX WIRELESS DRIVER (SOFTMAC BASED VERSION)
P: Larry Finger
M: Larry.Finger@lwfinger.net
P: Stefano Brivio
M: stefano.brivio@polimi.it
L: linux-wireless@vger.kernel.org
W: http://bcm43xx.berlios.de/
S: Obsolete
BEFS FILE SYSTEM
P: Sergey S. Kostyliov
M: rathamahata@php4.ru
......
......@@ -741,7 +741,6 @@ config P54_PCI
source "drivers/net/wireless/ath5k/Kconfig"
source "drivers/net/wireless/iwlwifi/Kconfig"
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"
......
......@@ -35,7 +35,6 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
obj-$(CONFIG_PRISM54) += prism54/
obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_BCM43XX) += bcm43xx/
obj-$(CONFIG_B43) += b43/
obj-$(CONFIG_B43LEGACY) += b43legacy/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
......
config BCM43XX
tristate "Broadcom BCM43xx wireless support (DEPRECATED)"
depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && (!SSB_B43_PCI_BRIDGE || SSB != y) && EXPERIMENTAL
select WIRELESS_EXT
select FW_LOADER
select HW_RANDOM
---help---
This is an experimental driver for the Broadcom 43xx wireless
chip, found in the Apple Airport Extreme and various other
devices. This driver is deprecated and will be removed
from the kernel in the near future. It has been replaced
by the b43 and b43legacy drivers.
config BCM43XX_DEBUG
bool "Broadcom BCM43xx debugging (RECOMMENDED)"
depends on BCM43XX
default y
---help---
Broadcom 43xx debugging messages.
Say Y, because the driver is still very experimental and
this will help you get it running.
config BCM43XX_DMA
bool
depends on BCM43XX
config BCM43XX_PIO
bool
depends on BCM43XX
choice
prompt "BCM43xx data transfer mode"
depends on BCM43XX
default BCM43XX_DMA_AND_PIO_MODE
config BCM43XX_DMA_AND_PIO_MODE
bool "DMA + PIO"
select BCM43XX_DMA
select BCM43XX_PIO
---help---
Include both, Direct Memory Access (DMA) and Programmed I/O (PIO)
data transfer modes.
The actually used mode is selectable through the module
parameter "pio". If the module parameter is pio=0, DMA is used.
Otherwise PIO is used. DMA is default.
If unsure, choose this option.
config BCM43XX_DMA_MODE
bool "DMA (Direct Memory Access) only"
select BCM43XX_DMA
---help---
Only include Direct Memory Access (DMA).
This reduces the size of the driver module, by omitting the PIO code.
config BCM43XX_PIO_MODE
bool "PIO (Programmed I/O) only"
select BCM43XX_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 43xx series support PIO.
The 4306 (Apple Airport Extreme and others) supports PIO, while
the 4318 is known to _not_ support PIO.
Only use PIO, if DMA does not work for you.
endchoice
obj-$(CONFIG_BCM43XX) += bcm43xx.o
bcm43xx-obj-$(CONFIG_BCM43XX_DEBUG) += bcm43xx_debugfs.o
bcm43xx-obj-$(CONFIG_BCM43XX_DMA) += bcm43xx_dma.o
bcm43xx-obj-$(CONFIG_BCM43XX_PIO) += bcm43xx_pio.o
bcm43xx-objs := bcm43xx_main.o bcm43xx_ilt.o \
bcm43xx_radio.o bcm43xx_phy.o \
bcm43xx_power.o bcm43xx_wx.o \
bcm43xx_leds.o bcm43xx_ethtool.o \
bcm43xx_xmit.o bcm43xx_sysfs.o \
$(bcm43xx-obj-y)
#ifndef BCM43xx_H_
#define BCM43xx_H_
#include <linux/hw_random.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/stringify.h>
#include <linux/pci.h>
#include <net/ieee80211.h>
#include <net/ieee80211softmac.h>
#include <asm/atomic.h>
#include <asm/io.h>
#include "bcm43xx_debugfs.h"
#include "bcm43xx_leds.h"
#define PFX KBUILD_MODNAME ": "
#define BCM43xx_SWITCH_CORE_MAX_RETRIES 50
#define BCM43xx_IRQWAIT_MAX_RETRIES 100
#define BCM43xx_IO_SIZE 8192
/* Active Core PCI Configuration Register. */
#define BCM43xx_PCICFG_ACTIVE_CORE 0x80
/* SPROM control register. */
#define BCM43xx_PCICFG_SPROMCTL 0x88
/* Interrupt Control PCI Configuration Register. (Only on PCI cores with rev >= 6) */
#define BCM43xx_PCICFG_ICR 0x94
/* MMIO offsets */
#define BCM43xx_MMIO_DMA0_REASON 0x20
#define BCM43xx_MMIO_DMA0_IRQ_MASK 0x24
#define BCM43xx_MMIO_DMA1_REASON 0x28
#define BCM43xx_MMIO_DMA1_IRQ_MASK 0x2C
#define BCM43xx_MMIO_DMA2_REASON 0x30
#define BCM43xx_MMIO_DMA2_IRQ_MASK 0x34
#define BCM43xx_MMIO_DMA3_REASON 0x38
#define BCM43xx_MMIO_DMA3_IRQ_MASK 0x3C
#define BCM43xx_MMIO_DMA4_REASON 0x40
#define BCM43xx_MMIO_DMA4_IRQ_MASK 0x44
#define BCM43xx_MMIO_DMA5_REASON 0x48
#define BCM43xx_MMIO_DMA5_IRQ_MASK 0x4C
#define BCM43xx_MMIO_STATUS_BITFIELD 0x120
#define BCM43xx_MMIO_STATUS2_BITFIELD 0x124
#define BCM43xx_MMIO_GEN_IRQ_REASON 0x128
#define BCM43xx_MMIO_GEN_IRQ_MASK 0x12C
#define BCM43xx_MMIO_RAM_CONTROL 0x130
#define BCM43xx_MMIO_RAM_DATA 0x134
#define BCM43xx_MMIO_PS_STATUS 0x140
#define BCM43xx_MMIO_RADIO_HWENABLED_HI 0x158
#define BCM43xx_MMIO_SHM_CONTROL 0x160
#define BCM43xx_MMIO_SHM_DATA 0x164
#define BCM43xx_MMIO_SHM_DATA_UNALIGNED 0x166
#define BCM43xx_MMIO_XMITSTAT_0 0x170
#define BCM43xx_MMIO_XMITSTAT_1 0x174
#define BCM43xx_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */
#define BCM43xx_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */
/* 32-bit DMA */
#define BCM43xx_MMIO_DMA32_BASE0 0x200
#define BCM43xx_MMIO_DMA32_BASE1 0x220
#define BCM43xx_MMIO_DMA32_BASE2 0x240
#define BCM43xx_MMIO_DMA32_BASE3 0x260
#define BCM43xx_MMIO_DMA32_BASE4 0x280
#define BCM43xx_MMIO_DMA32_BASE5 0x2A0
/* 64-bit DMA */
#define BCM43xx_MMIO_DMA64_BASE0 0x200
#define BCM43xx_MMIO_DMA64_BASE1 0x240
#define BCM43xx_MMIO_DMA64_BASE2 0x280
#define BCM43xx_MMIO_DMA64_BASE3 0x2C0
#define BCM43xx_MMIO_DMA64_BASE4 0x300
#define BCM43xx_MMIO_DMA64_BASE5 0x340
/* PIO */
#define BCM43xx_MMIO_PIO1_BASE 0x300
#define BCM43xx_MMIO_PIO2_BASE 0x310
#define BCM43xx_MMIO_PIO3_BASE 0x320
#define BCM43xx_MMIO_PIO4_BASE 0x330
#define BCM43xx_MMIO_PHY_VER 0x3E0
#define BCM43xx_MMIO_PHY_RADIO 0x3E2
#define BCM43xx_MMIO_ANTENNA 0x3E8
#define BCM43xx_MMIO_CHANNEL 0x3F0
#define BCM43xx_MMIO_CHANNEL_EXT 0x3F4
#define BCM43xx_MMIO_RADIO_CONTROL 0x3F6
#define BCM43xx_MMIO_RADIO_DATA_HIGH 0x3F8
#define BCM43xx_MMIO_RADIO_DATA_LOW 0x3FA
#define BCM43xx_MMIO_PHY_CONTROL 0x3FC
#define BCM43xx_MMIO_PHY_DATA 0x3FE
#define BCM43xx_MMIO_MACFILTER_CONTROL 0x420
#define BCM43xx_MMIO_MACFILTER_DATA 0x422
#define BCM43xx_MMIO_RADIO_HWENABLED_LO 0x49A
#define BCM43xx_MMIO_GPIO_CONTROL 0x49C
#define BCM43xx_MMIO_GPIO_MASK 0x49E
#define BCM43xx_MMIO_TSF_0 0x632 /* core rev < 3 only */
#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */
#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */
#define BCM43xx_MMIO_RNG 0x65A
#define BCM43xx_MMIO_POWERUP_DELAY 0x6A8
/* SPROM offsets. */
#define BCM43xx_SPROM_BASE 0x1000
#define BCM43xx_SPROM_BOARDFLAGS2 0x1c
#define BCM43xx_SPROM_IL0MACADDR 0x24
#define BCM43xx_SPROM_ET0MACADDR 0x27
#define BCM43xx_SPROM_ET1MACADDR 0x2a
#define BCM43xx_SPROM_ETHPHY 0x2d
#define BCM43xx_SPROM_BOARDREV 0x2e
#define BCM43xx_SPROM_PA0B0 0x2f
#define BCM43xx_SPROM_PA0B1 0x30
#define BCM43xx_SPROM_PA0B2 0x31
#define BCM43xx_SPROM_WL0GPIO0 0x32
#define BCM43xx_SPROM_WL0GPIO2 0x33
#define BCM43xx_SPROM_MAXPWR 0x34
#define BCM43xx_SPROM_PA1B0 0x35
#define BCM43xx_SPROM_PA1B1 0x36
#define BCM43xx_SPROM_PA1B2 0x37
#define BCM43xx_SPROM_IDL_TSSI_TGT 0x38
#define BCM43xx_SPROM_BOARDFLAGS 0x39
#define BCM43xx_SPROM_ANTENNA_GAIN 0x3a
#define BCM43xx_SPROM_VERSION 0x3f
/* BCM43xx_SPROM_BOARDFLAGS values */
#define BCM43xx_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */
#define BCM43xx_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */
#define BCM43xx_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */
#define BCM43xx_BFL_RSSI 0x0008 /* software calculates nrssi slope. */
#define BCM43xx_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */
#define BCM43xx_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */
#define BCM43xx_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */
#define BCM43xx_BFL_ENETADM 0x0080 /* has ADMtek switch */
#define BCM43xx_BFL_ENETVLAN 0x0100 /* can do vlan */
#define BCM43xx_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */
#define BCM43xx_BFL_NOPCI 0x0400 /* leaves PCI floating */
#define BCM43xx_BFL_FEM 0x0800 /* supports the Front End Module */
#define BCM43xx_BFL_EXTLNA 0x1000 /* has an external LNA */
#define BCM43xx_BFL_HGPA 0x2000 /* had high gain PA */
#define BCM43xx_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */
#define BCM43xx_BFL_ALTIQ 0x8000 /* alternate I/Q settings */
/* GPIO register offset, in both ChipCommon and PCI core. */
#define BCM43xx_GPIO_CONTROL 0x6c
/* SHM Routing */
#define BCM43xx_SHM_SHARED 0x0001
#define BCM43xx_SHM_WIRELESS 0x0002
#define BCM43xx_SHM_PCM 0x0003
#define BCM43xx_SHM_HWMAC 0x0004
#define BCM43xx_SHM_UCODE 0x0300
/* MacFilter offsets. */
#define BCM43xx_MACFILTER_SELF 0x0000
#define BCM43xx_MACFILTER_ASSOC 0x0003
/* Chipcommon registers. */
#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04
#define BCM43xx_CHIPCOMMON_CTL 0x28
#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0
#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4
#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8
#define BCM43xx_CHIPCOMMON_SYSCLKCTL 0xC0
/* PCI core specific registers. */
#define BCM43xx_PCICORE_BCAST_ADDR 0x50
#define BCM43xx_PCICORE_BCAST_DATA 0x54
#define BCM43xx_PCICORE_SBTOPCI2 0x108
/* SBTOPCI2 values. */
#define BCM43xx_SBTOPCI2_PREFETCH 0x4
#define BCM43xx_SBTOPCI2_BURST 0x8
#define BCM43xx_SBTOPCI2_MEMREAD_MULTI 0x20
/* PCI-E core registers. */
#define BCM43xx_PCIECORE_REG_ADDR 0x0130
#define BCM43xx_PCIECORE_REG_DATA 0x0134
#define BCM43xx_PCIECORE_MDIO_CTL 0x0128
#define BCM43xx_PCIECORE_MDIO_DATA 0x012C
/* PCI-E registers. */
#define BCM43xx_PCIE_TLP_WORKAROUND 0x0004
#define BCM43xx_PCIE_DLLP_LINKCTL 0x0100
/* PCI-E MDIO bits. */
#define BCM43xx_PCIE_MDIO_ST 0x40000000
#define BCM43xx_PCIE_MDIO_WT 0x10000000
#define BCM43xx_PCIE_MDIO_DEV 22
#define BCM43xx_PCIE_MDIO_REG 18
#define BCM43xx_PCIE_MDIO_TA 0x00020000
#define BCM43xx_PCIE_MDIO_TC 0x0100
/* MDIO devices. */
#define BCM43xx_MDIO_SERDES_RX 0x1F
/* SERDES RX registers. */
#define BCM43xx_SERDES_RXTIMER 0x2
#define BCM43xx_SERDES_CDR 0x6
#define BCM43xx_SERDES_CDR_BW 0x7
/* Chipcommon capabilities. */
#define BCM43xx_CAPABILITIES_PCTL 0x00040000
#define BCM43xx_CAPABILITIES_PLLMASK 0x00030000
#define BCM43xx_CAPABILITIES_PLLSHIFT 16
#define BCM43xx_CAPABILITIES_FLASHMASK 0x00000700
#define BCM43xx_CAPABILITIES_FLASHSHIFT 8
#define BCM43xx_CAPABILITIES_EXTBUSPRESENT 0x00000040
#define BCM43xx_CAPABILITIES_UARTGPIO 0x00000020
#define BCM43xx_CAPABILITIES_UARTCLOCKMASK 0x00000018
#define BCM43xx_CAPABILITIES_UARTCLOCKSHIFT 3
#define BCM43xx_CAPABILITIES_MIPSBIGENDIAN 0x00000004
#define BCM43xx_CAPABILITIES_NRUARTSMASK 0x00000003
/* PowerControl */
#define BCM43xx_PCTL_IN 0xB0
#define BCM43xx_PCTL_OUT 0xB4
#define BCM43xx_PCTL_OUTENABLE 0xB8
#define BCM43xx_PCTL_XTAL_POWERUP 0x40
#define BCM43xx_PCTL_PLL_POWERDOWN 0x80
/* PowerControl Clock Modes */
#define BCM43xx_PCTL_CLK_FAST 0x00
#define BCM43xx_PCTL_CLK_SLOW 0x01
#define BCM43xx_PCTL_CLK_DYNAMIC 0x02
#define BCM43xx_PCTL_FORCE_SLOW 0x0800
#define BCM43xx_PCTL_FORCE_PLL 0x1000
#define BCM43xx_PCTL_DYN_XTAL 0x2000
/* COREIDs */
#define BCM43xx_COREID_CHIPCOMMON 0x800
#define BCM43xx_COREID_ILINE20 0x801
#define BCM43xx_COREID_SDRAM 0x803
#define BCM43xx_COREID_PCI 0x804
#define BCM43xx_COREID_MIPS 0x805
#define BCM43xx_COREID_ETHERNET 0x806
#define BCM43xx_COREID_V90 0x807
#define BCM43xx_COREID_USB11_HOSTDEV 0x80a
#define BCM43xx_COREID_IPSEC 0x80b
#define BCM43xx_COREID_PCMCIA 0x80d
#define BCM43xx_COREID_EXT_IF 0x80f
#define BCM43xx_COREID_80211 0x812
#define BCM43xx_COREID_MIPS_3302 0x816
#define BCM43xx_COREID_USB11_HOST 0x817
#define BCM43xx_COREID_USB11_DEV 0x818
#define BCM43xx_COREID_USB20_HOST 0x819
#define BCM43xx_COREID_USB20_DEV 0x81a
#define BCM43xx_COREID_SDIO_HOST 0x81b
#define BCM43xx_COREID_PCIE 0x820
/* Core Information Registers */
#define BCM43xx_CIR_BASE 0xf00
#define BCM43xx_CIR_SBTPSFLAG (BCM43xx_CIR_BASE + 0x18)
#define BCM43xx_CIR_SBIMSTATE (BCM43xx_CIR_BASE + 0x90)
#define BCM43xx_CIR_SBINTVEC (BCM43xx_CIR_BASE + 0x94)
#define BCM43xx_CIR_SBTMSTATELOW (BCM43xx_CIR_BASE + 0x98)
#define BCM43xx_CIR_SBTMSTATEHIGH (BCM43xx_CIR_BASE + 0x9c)
#define BCM43xx_CIR_SBIMCONFIGLOW (BCM43xx_CIR_BASE + 0xa8)
#define BCM43xx_CIR_SB_ID_HI (BCM43xx_CIR_BASE + 0xfc)
/* Mask to get the Backplane Flag Number from SBTPSFLAG. */
#define BCM43xx_BACKPLANE_FLAG_NR_MASK 0x3f
/* SBIMCONFIGLOW values/masks. */
#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK 0x00000007
#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT 0
#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK 0x00000070
#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT 4
#define BCM43xx_SBIMCONFIGLOW_CONNID_MASK 0x00ff0000
#define BCM43xx_SBIMCONFIGLOW_CONNID_SHIFT 16
/* sbtmstatelow state flags */
#define BCM43xx_SBTMSTATELOW_RESET 0x01
#define BCM43xx_SBTMSTATELOW_REJECT 0x02
#define BCM43xx_SBTMSTATELOW_CLOCK 0x10000
#define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000
#define BCM43xx_SBTMSTATELOW_G_MODE_ENABLE 0x20000000
/* sbtmstatehigh state flags */
#define BCM43xx_SBTMSTATEHIGH_SERROR 0x00000001
#define BCM43xx_SBTMSTATEHIGH_BUSY 0x00000004
#define BCM43xx_SBTMSTATEHIGH_TIMEOUT 0x00000020
#define BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000
#define BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL 0x00020000
#define BCM43xx_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000
#define BCM43xx_SBTMSTATEHIGH_DMA64BIT 0x10000000
#define BCM43xx_SBTMSTATEHIGH_GATEDCLK 0x20000000
#define BCM43xx_SBTMSTATEHIGH_BISTFAILED 0x40000000
#define BCM43xx_SBTMSTATEHIGH_BISTCOMPLETE 0x80000000
/* sbimstate flags */
#define BCM43xx_SBIMSTATE_IB_ERROR 0x20000
#define BCM43xx_SBIMSTATE_TIMEOUT 0x40000
/* PHYVersioning */
#define BCM43xx_PHYTYPE_A 0x00
#define BCM43xx_PHYTYPE_B 0x01
#define BCM43xx_PHYTYPE_G 0x02
/* PHYRegisters */
#define BCM43xx_PHY_ILT_A_CTRL 0x0072
#define BCM43xx_PHY_ILT_A_DATA1 0x0073
#define BCM43xx_PHY_ILT_A_DATA2 0x0074
#define BCM43xx_PHY_G_LO_CONTROL 0x0810
#define BCM43xx_PHY_ILT_G_CTRL 0x0472
#define BCM43xx_PHY_ILT_G_DATA1 0x0473
#define BCM43xx_PHY_ILT_G_DATA2 0x0474
#define BCM43xx_PHY_A_PCTL 0x007B
#define BCM43xx_PHY_G_PCTL 0x0029
#define BCM43xx_PHY_A_CRS 0x0029
#define BCM43xx_PHY_RADIO_BITFIELD 0x0401
#define BCM43xx_PHY_G_CRS 0x0429
#define BCM43xx_PHY_NRSSILT_CTRL 0x0803
#define BCM43xx_PHY_NRSSILT_DATA 0x0804
/* RadioRegisters */
#define BCM43xx_RADIOCTL_ID 0x01
/* StatusBitField */
#define BCM43xx_SBF_MAC_ENABLED 0x00000001
#define BCM43xx_SBF_2 0x00000002 /*FIXME: fix name*/
#define BCM43xx_SBF_CORE_READY 0x00000004
#define BCM43xx_SBF_400 0x00000400 /*FIXME: fix name*/
#define BCM43xx_SBF_4000 0x00004000 /*FIXME: fix name*/
#define BCM43xx_SBF_8000 0x00008000 /*FIXME: fix name*/
#define BCM43xx_SBF_XFER_REG_BYTESWAP 0x00010000
#define BCM43xx_SBF_MODE_NOTADHOC 0x00020000
#define BCM43xx_SBF_MODE_AP 0x00040000
#define BCM43xx_SBF_RADIOREG_LOCK 0x00080000
#define BCM43xx_SBF_MODE_MONITOR 0x00400000
#define BCM43xx_SBF_MODE_PROMISC 0x01000000
#define BCM43xx_SBF_PS1 0x02000000
#define BCM43xx_SBF_PS2 0x04000000
#define BCM43xx_SBF_NO_SSID_BCAST 0x08000000
#define BCM43xx_SBF_TIME_UPDATE 0x10000000
#define BCM43xx_SBF_MODE_G 0x80000000
/* Microcode */
#define BCM43xx_UCODE_REVISION 0x0000
#define BCM43xx_UCODE_PATCHLEVEL 0x0002
#define BCM43xx_UCODE_DATE 0x0004
#define BCM43xx_UCODE_TIME 0x0006
#define BCM43xx_UCODE_STATUS 0x0040
/* MicrocodeFlagsBitfield (addr + lo-word values?)*/
#define BCM43xx_UCODEFLAGS_OFFSET 0x005E
#define BCM43xx_UCODEFLAG_AUTODIV 0x0001
#define BCM43xx_UCODEFLAG_UNKBGPHY 0x0002
#define BCM43xx_UCODEFLAG_UNKBPHY 0x0004
#define BCM43xx_UCODEFLAG_UNKGPHY 0x0020
#define BCM43xx_UCODEFLAG_UNKPACTRL 0x0040
#define BCM43xx_UCODEFLAG_JAPAN 0x0080
/* Hardware Radio Enable masks */
#define BCM43xx_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16)
#define BCM43xx_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4)
/* Generic-Interrupt reasons. */
#define BCM43xx_IRQ_READY (1 << 0)
#define BCM43xx_IRQ_BEACON (1 << 1)
#define BCM43xx_IRQ_PS (1 << 2)
#define BCM43xx_IRQ_REG124 (1 << 5)
#define BCM43xx_IRQ_PMQ (1 << 6)
#define BCM43xx_IRQ_PIO_WORKAROUND (1 << 8)
#define BCM43xx_IRQ_XMIT_ERROR (1 << 11)
#define BCM43xx_IRQ_RX (1 << 15)
#define BCM43xx_IRQ_SCAN (1 << 16)
#define BCM43xx_IRQ_NOISE (1 << 18)
#define BCM43xx_IRQ_XMIT_STATUS (1 << 29)
#define BCM43xx_IRQ_ALL 0xffffffff
#define BCM43xx_IRQ_INITIAL (BCM43xx_IRQ_PS | \
BCM43xx_IRQ_REG124 | \
BCM43xx_IRQ_PMQ | \
BCM43xx_IRQ_XMIT_ERROR | \
BCM43xx_IRQ_RX | \
BCM43xx_IRQ_SCAN | \
BCM43xx_IRQ_NOISE | \
BCM43xx_IRQ_XMIT_STATUS)
/* Initial default iw_mode */
#define BCM43xx_INITIAL_IWMODE IW_MODE_INFRA
/* Bus type PCI. */
#define BCM43xx_BUSTYPE_PCI 0
/* Bus type Silicone Backplane Bus. */
#define BCM43xx_BUSTYPE_SB 1
/* Bus type PCMCIA. */
#define BCM43xx_BUSTYPE_PCMCIA 2
/* Threshold values. */
#define BCM43xx_MIN_RTS_THRESHOLD 1U
#define BCM43xx_MAX_RTS_THRESHOLD 2304U
#define BCM43xx_DEFAULT_RTS_THRESHOLD BCM43xx_MAX_RTS_THRESHOLD
#define BCM43xx_DEFAULT_SHORT_RETRY_LIMIT 7
#define BCM43xx_DEFAULT_LONG_RETRY_LIMIT 4
/* FIXME: the next line is a guess as to what the maximum RSSI value might be */
#define RX_RSSI_MAX 60
/* Max size of a security key */
#define BCM43xx_SEC_KEYSIZE 16
/* Security algorithms. */
enum {
BCM43xx_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */
BCM43xx_SEC_ALGO_WEP,
BCM43xx_SEC_ALGO_UNKNOWN,
BCM43xx_SEC_ALGO_AES,
BCM43xx_SEC_ALGO_WEP104,
BCM43xx_SEC_ALGO_TKIP,
};
#ifdef assert
# undef assert
#endif
#ifdef CONFIG_BCM43XX_DEBUG
#define assert(expr) \
do { \
if (unlikely(!(expr))) { \
printk(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", \
#expr, __FILE__, __LINE__, __FUNCTION__); \
} \
} while (0)
#else
#define assert(expr) do { /* nothing */ } while (0)
#endif
/* rate limited printk(). */
#ifdef printkl
# undef printkl
#endif
#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
/* rate limited printk() for debugging */
#ifdef dprintkl
# undef dprintkl
#endif
#ifdef CONFIG_BCM43XX_DEBUG
# define dprintkl printkl
#else
# define dprintkl(f, x...) do { /* nothing */ } while (0)
#endif
/* Helper macro for if branches.
* An if branch marked with this macro is only taken in DEBUG mode.
* Example:
* if (DEBUG_ONLY(foo == bar)) {
* do something
* }
* In DEBUG mode, the branch will be taken if (foo == bar).
* In non-DEBUG mode, the branch will never be taken.
*/
#ifdef DEBUG_ONLY
# undef DEBUG_ONLY
#endif
#ifdef CONFIG_BCM43XX_DEBUG
# define DEBUG_ONLY(x) (x)
#else
# define DEBUG_ONLY(x) 0
#endif
/* debugging printk() */
#ifdef dprintk
# undef dprintk
#endif
#ifdef CONFIG_BCM43XX_DEBUG
# define dprintk(f, x...) do { printk(f ,##x); } while (0)
#else
# define dprintk(f, x...) do { /* nothing */ } while (0)
#endif
struct net_device;
struct pci_dev;
struct bcm43xx_dmaring;
struct bcm43xx_pioqueue;
struct bcm43xx_initval {
__be16 offset;
__be16 size;
__be32 value;
} __attribute__((__packed__));
/* Values for bcm430x_sprominfo.locale */
enum {
BCM43xx_LOCALE_WORLD = 0,
BCM43xx_LOCALE_THAILAND,
BCM43xx_LOCALE_ISRAEL,
BCM43xx_LOCALE_JORDAN,
BCM43xx_LOCALE_CHINA,
BCM43xx_LOCALE_JAPAN,
BCM43xx_LOCALE_USA_CANADA_ANZ,
BCM43xx_LOCALE_EUROPE,
BCM43xx_LOCALE_USA_LOW,
BCM43xx_LOCALE_JAPAN_HIGH,
BCM43xx_LOCALE_ALL,
BCM43xx_LOCALE_NONE,
};
#define BCM43xx_SPROM_SIZE 64 /* in 16-bit words. */
struct bcm43xx_sprominfo {
u16 boardflags2;
u8 il0macaddr[6];
u8 et0macaddr[6];
u8 et1macaddr[6];
u8 et0phyaddr:5;
u8 et1phyaddr:5;
u8 boardrev;
u8 locale:4;
u8 antennas_aphy:2;
u8 antennas_bgphy:2;
u16 pa0b0;
u16 pa0b1;
u16 pa0b2;
u8 wl0gpio0;
u8 wl0gpio1;
u8 wl0gpio2;
u8 wl0gpio3;
u8 maxpower_aphy;
u8 maxpower_bgphy;
u16 pa1b0;
u16 pa1b1;
u16 pa1b2;
u8 idle_tssi_tgt_aphy;
u8 idle_tssi_tgt_bgphy;
u16 boardflags;
u16 antennagain_aphy;
u16 antennagain_bgphy;
};
/* Value pair to measure the LocalOscillator. */
struct bcm43xx_lopair {
s8 low;
s8 high;
u8 used:1;
};
#define BCM43xx_LO_COUNT (14*4)
struct bcm43xx_phyinfo {
/* Hardware Data */
u8 analog;
u8 type;
u8 rev;
u16 antenna_diversity;
u16 savedpctlreg;
u16 minlowsig[2];
u16 minlowsigpos[2];
u8 connected:1,
calibrated:1,
is_locked:1, /* used in bcm43xx_phy_{un}lock() */
dyn_tssi_tbl:1; /* used in bcm43xx_phy_init_tssi2dbm_table() */
/* LO Measurement Data.
* Use bcm43xx_get_lopair() to get a value.
*/
struct bcm43xx_lopair *_lo_pairs;
/* TSSI to dBm table in use */
const s8 *tssi2dbm;
/* idle TSSI value */
s8 idle_tssi;
/* Values from bcm43xx_calc_loopback_gain() */
u16 loopback_gain[2];
/* PHY lock for core.rev < 3
* This lock is only used by bcm43xx_phy_{un}lock()
*/
spinlock_t lock;
/* Firmware. */
const struct firmware *ucode;
const struct firmware *pcm;
const struct firmware *initvals0;
const struct firmware *initvals1;
};
struct bcm43xx_radioinfo {
u16 manufact;
u16 version;
u8 revision;
/* Desired TX power in dBm Q5.2 */
u16 txpower_desired;
/* TX Power control values. */
union {
/* B/G PHY */
struct {
u16 baseband_atten;
u16 radio_atten;
u16 txctl1;
u16 txctl2;
};
/* A PHY */
struct {
u16 txpwr_offset;
};
};
/* 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 BCM43xx_INTERFSTACK_SIZE 26
u32 interfstack[BCM43xx_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;
u8 initial_channel;
u16 lofcal;
u16 initval;
u8 enabled:1;
/* ACI (adjacent channel interference) flags. */
u8 aci_enable:1,
aci_wlan_automatic:1,
aci_hw_rssi:1;
};
/* Data structures for DMA transmission, per 80211 core. */
struct bcm43xx_dma {
struct bcm43xx_dmaring *tx_ring0;
struct bcm43xx_dmaring *tx_ring1;
struct bcm43xx_dmaring *tx_ring2;
struct bcm43xx_dmaring *tx_ring3;
struct bcm43xx_dmaring *tx_ring4;
struct bcm43xx_dmaring *tx_ring5;
struct bcm43xx_dmaring *rx_ring0;
struct bcm43xx_dmaring *rx_ring3; /* only available on core.rev < 5 */
};
/* Data structures for PIO transmission, per 80211 core. */
struct bcm43xx_pio {
struct bcm43xx_pioqueue *queue0;
struct bcm43xx_pioqueue *queue1;
struct bcm43xx_pioqueue *queue2;
struct bcm43xx_pioqueue *queue3;
};
#define BCM43xx_MAX_80211_CORES 2
/* Generic information about a core. */
struct bcm43xx_coreinfo {
u8 available:1,
enabled:1,
initialized:1;
/** core_rev revision number */
u8 rev;
/** Index number for _switch_core() */
u8 index;
/** core_id ID number */
u16 id;
/** Core-specific data. */
void *priv;
};
/* Additional information for each 80211 core. */
struct bcm43xx_coreinfo_80211 {
/* PHY device. */
struct bcm43xx_phyinfo phy;
/* Radio device. */
struct bcm43xx_radioinfo radio;
union {
/* DMA context. */
struct bcm43xx_dma dma;
/* PIO context. */
struct bcm43xx_pio pio;
};
};
/* Context information for a noise calculation (Link Quality). */
struct bcm43xx_noise_calculation {
struct bcm43xx_coreinfo *core_at_start;
u8 channel_at_start;
u8 calculation_running:1;
u8 nr_samples;
s8 samples[8][4];
};
struct bcm43xx_stats {
u8 noise;
struct iw_statistics wstats;
/* Store the last TX/RX times here for updating the leds. */
unsigned long last_tx;
unsigned long last_rx;
};
struct bcm43xx_key {
u8 enabled:1;
u8 algorithm;
};
/* Driver initialization status. */
enum {
BCM43xx_STAT_UNINIT, /* Uninitialized. */
BCM43xx_STAT_INITIALIZING, /* init_board() in progress. */
BCM43xx_STAT_INITIALIZED, /* Fully operational. */
BCM43xx_STAT_SHUTTINGDOWN, /* free_board() in progress. */
BCM43xx_STAT_RESTARTING, /* controller_restart() called. */
};
#define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status)
#define bcm43xx_set_status(bcm, stat) do { \
atomic_set(&(bcm)->init_status, (stat)); \
smp_wmb(); \
} while (0)
/* *** THEORY OF LOCKING ***
*
* We have two different locks in the bcm43xx driver.
* => bcm->mutex: General sleeping mutex. Protects struct bcm43xx_private
* and the device registers. This mutex does _not_ protect
* against concurrency from the IRQ handler.
* => bcm->irq_lock: IRQ spinlock. Protects against IRQ handler concurrency.
*
* Please note that, if you only take the irq_lock, you are not protected
* against concurrency from the periodic work handlers.
* Most times you want to take _both_ locks.
*/
struct bcm43xx_private {
struct ieee80211_device *ieee;
struct ieee80211softmac_device *softmac;
struct net_device *net_dev;
struct pci_dev *pci_dev;
unsigned int irq;
void __iomem *mmio_addr;
spinlock_t irq_lock;
struct mutex mutex;
/* Driver initialization status BCM43xx_STAT_*** */
atomic_t init_status;
u16 was_initialized:1, /* for PCI suspend/resume. */
__using_pio:1, /* Internal, use bcm43xx_using_pio(). */
bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */
reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */
short_preamble:1, /* TRUE, if short preamble is enabled. */
firmware_norelease:1, /* Do not release the firmware. Used on suspend. */
radio_hw_enable:1; /* TRUE if radio is hardware enabled */
struct bcm43xx_stats stats;
/* Bus type we are connected to.
* This is currently always BCM43xx_BUSTYPE_PCI
*/
u8 bustype;
u64 dma_mask;
u16 board_vendor;
u16 board_type;
u16 board_revision;
u16 chip_id;
u8 chip_rev;
u8 chip_package;
struct bcm43xx_sprominfo sprom;
#define BCM43xx_NR_LEDS 4
struct bcm43xx_led leds[BCM43xx_NR_LEDS];
spinlock_t leds_lock;
/* The currently active core. */
struct bcm43xx_coreinfo *current_core;
struct bcm43xx_coreinfo *active_80211_core;
/* coreinfo structs for all possible cores follow.
* Note that a core might not exist.
* So check the coreinfo flags before using it.
*/
struct bcm43xx_coreinfo core_chipcommon;
struct bcm43xx_coreinfo core_pci;
struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ];
/* Additional information, specific to the 80211 cores. */
struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ];
/* Number of available 80211 cores. */
int nr_80211_available;
u32 chipcommon_capabilities;
/* 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 bcm43xx_noise_calculation noisecalc;
/* if > 0 MAC is suspended. if == 0 MAC is enabled. */
int mac_suspended;
/* Threshold values. */
//TODO: The RTS thr has to be _used_. Currently, it is only set via WX.
u32 rts_threshold;
/* 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;
/* Informational stuff. */
char nick[IW_ESSID_MAX_SIZE + 1];
/* encryption/decryption */
u16 security_offset;
struct bcm43xx_key key[54];
u8 default_key_idx;
/* Random Number Generator. */
struct hwrng rng;
char rng_name[20 + 1];
/* Debugging stuff follows. */
#ifdef CONFIG_BCM43XX_DEBUG
struct bcm43xx_dfsentry *dfsentry;
#endif
};
static inline
struct bcm43xx_private * bcm43xx_priv(struct net_device *dev)
{
return ieee80211softmac_priv(dev);
}
struct device;
static inline
struct bcm43xx_private * dev_to_bcm(struct device *dev)
{
struct net_device *net_dev;
struct bcm43xx_private *bcm;
net_dev = dev_get_drvdata(dev);
bcm = bcm43xx_priv(net_dev);
return bcm;
}
/* Helper function, which returns a boolean.
* TRUE, if PIO is used; FALSE, if DMA is used.
*/
#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO)
static inline
int bcm43xx_using_pio(struct bcm43xx_private *bcm)
{
return bcm->__using_pio;
}
#elif defined(CONFIG_BCM43XX_DMA)
static inline
int bcm43xx_using_pio(struct bcm43xx_private *bcm)
{
return 0;
}
#elif defined(CONFIG_BCM43XX_PIO)
static inline
int bcm43xx_using_pio(struct bcm43xx_private *bcm)
{
return 1;
}
#else
# error "Using neither DMA nor PIO? Confused..."
#endif
/* Helper functions to access data structures private to the 80211 cores.
* Note that we _must_ have an 80211 core mapped when calling
* any of these functions.
*/
static inline
struct bcm43xx_coreinfo_80211 *
bcm43xx_current_80211_priv(struct bcm43xx_private *bcm)
{
assert(bcm->current_core->id == BCM43xx_COREID_80211);
return bcm->current_core->priv;
}
static inline
struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm)
{
assert(bcm43xx_using_pio(bcm));
return &(bcm43xx_current_80211_priv(bcm)->pio);
}
static inline
struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm)
{
assert(!bcm43xx_using_pio(bcm));
return &(bcm43xx_current_80211_priv(bcm)->dma);
}
static inline
struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm)
{
return &(bcm43xx_current_80211_priv(bcm)->phy);
}
static inline
struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm)
{
return &(bcm43xx_current_80211_priv(bcm)->radio);
}
static inline
struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy,
u16 radio_attenuation,
u16 baseband_attenuation)
{
return phy->_lo_pairs + (radio_attenuation + 14 * (baseband_attenuation / 2));
}
static inline
u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset)
{
return ioread16(bcm->mmio_addr + offset);
}
static inline
void bcm43xx_write16(struct bcm43xx_private *bcm, u16 offset, u16 value)
{
iowrite16(value, bcm->mmio_addr + offset);
}
static inline
u32 bcm43xx_read32(struct bcm43xx_private *bcm, u16 offset)
{
return ioread32(bcm->mmio_addr + offset);
}
static inline
void bcm43xx_write32(struct bcm43xx_private *bcm, u16 offset, u32 value)
{
iowrite32(value, bcm->mmio_addr + offset);
}
static inline
int bcm43xx_pci_read_config16(struct bcm43xx_private *bcm, int offset, u16 *value)
{
return pci_read_config_word(bcm->pci_dev, offset, value);
}
static inline
int bcm43xx_pci_read_config32(struct bcm43xx_private *bcm, int offset, u32 *value)
{
return pci_read_config_dword(bcm->pci_dev, offset, value);
}
static inline
int bcm43xx_pci_write_config16(struct bcm43xx_private *bcm, int offset, u16 value)
{
return pci_write_config_word(bcm->pci_dev, offset, value);
}
static inline
int bcm43xx_pci_write_config32(struct bcm43xx_private *bcm, int offset, u32 value)
{
return pci_write_config_dword(bcm->pci_dev, offset, value);
}
/** 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; \
})
#endif /* BCM43xx_H_ */
/*
Broadcom BCM43xx wireless driver
debugfs driver debugging code
Copyright (c) 2005 Michael Buesch <mbuesch@freenet.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 <asm/io.h>
#include "bcm43xx.h"
#include "bcm43xx_main.h"
#include "bcm43xx_debugfs.h"
#include "bcm43xx_dma.h"
#include "bcm43xx_pio.h"
#include "bcm43xx_xmit.h"
#define REALLY_BIG_BUFFER_SIZE (1024*256)
static struct bcm43xx_debugfs fs;
static char really_big_buffer[REALLY_BIG_BUFFER_SIZE];
static DECLARE_MUTEX(big_buffer_sem);
static ssize_t write_file_dummy(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return count;
}
static int open_file_generic(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
#define fappend(fmt, x...) pos += snprintf(buf + pos, len - pos, fmt , ##x)
static ssize_t devinfo_read_file(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const size_t len = REALLY_BIG_BUFFER_SIZE;
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
size_t pos = 0;
ssize_t res;
struct net_device *net_dev;
struct pci_dev *pci_dev;
unsigned long flags;
u16 tmp16;
int i;
down(&big_buffer_sem);
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
fappend("Board not initialized.\n");
goto out;
}
net_dev = bcm->net_dev;
pci_dev = bcm->pci_dev;
/* This is where the information is written to the "devinfo" file */
fappend("*** %s devinfo ***\n", net_dev->name);
fappend("vendor: 0x%04x device: 0x%04x\n",
pci_dev->vendor, pci_dev->device);
fappend("subsystem_vendor: 0x%04x subsystem_device: 0x%04x\n",
pci_dev->subsystem_vendor, pci_dev->subsystem_device);
fappend("IRQ: %d\n", bcm->irq);
fappend("mmio_addr: 0x%p\n", bcm->mmio_addr);
fappend("chip_id: 0x%04x chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev);
if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16)))
fappend("Radio disabled by hardware!\n");
if ((bcm->core_80211[0].rev < 3) && !(bcm43xx_read16(bcm, 0x049A) & (1 << 4)))
fappend("Radio disabled by hardware!\n");
fappend("board_vendor: 0x%04x board_type: 0x%04x\n", bcm->board_vendor,
bcm->board_type);
fappend("\nCores:\n");
#define fappend_core(name, info) fappend("core \"" name "\" %s, %s, id: 0x%04x, " \
"rev: 0x%02x, index: 0x%02x\n", \
(info).available \
? "available" : "nonavailable", \
(info).enabled \
? "enabled" : "disabled", \
(info).id, (info).rev, (info).index)
fappend_core("CHIPCOMMON", bcm->core_chipcommon);
fappend_core("PCI", bcm->core_pci);
fappend_core("first 80211", bcm->core_80211[0]);
fappend_core("second 80211", bcm->core_80211[1]);
#undef fappend_core
tmp16 = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
fappend("LEDs: ");
for (i = 0; i < BCM43xx_NR_LEDS; i++)
fappend("%d ", !!(tmp16 & (1 << i)));
fappend("\n");
out:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
up(&big_buffer_sem);
return res;
}
static ssize_t drvinfo_read_file(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const size_t len = REALLY_BIG_BUFFER_SIZE;
char *buf = really_big_buffer;
size_t pos = 0;
ssize_t res;
down(&big_buffer_sem);
/* This is where the information is written to the "driver" file */
fappend(KBUILD_MODNAME " driver\n");
fappend("Compiled at: %s %s\n", __DATE__, __TIME__);
res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
up(&big_buffer_sem);
return res;
}
static ssize_t spromdump_read_file(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const size_t len = REALLY_BIG_BUFFER_SIZE;
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
size_t pos = 0;
ssize_t res;
unsigned long flags;
down(&big_buffer_sem);
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
fappend("Board not initialized.\n");
goto out;
}
/* This is where the information is written to the "sprom_dump" file */
fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags);
out:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
up(&big_buffer_sem);
return res;
}
static ssize_t tsf_read_file(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const size_t len = REALLY_BIG_BUFFER_SIZE;
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
size_t pos = 0;
ssize_t res;
unsigned long flags;
u64 tsf;
down(&big_buffer_sem);
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
fappend("Board not initialized.\n");
goto out;
}
bcm43xx_tsf_read(bcm, &tsf);
fappend("0x%08x%08x\n",
(unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
(unsigned int)(tsf & 0xFFFFFFFFULL));
out:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
up(&big_buffer_sem);
return res;
}
static ssize_t tsf_write_file(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
ssize_t buf_size;
ssize_t res;
unsigned long flags;
unsigned long long tsf;
buf_size = min(count, sizeof (really_big_buffer) - 1);
down(&big_buffer_sem);
if (copy_from_user(buf, user_buf, buf_size)) {
res = -EFAULT;
goto out_up;
}
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
printk(KERN_INFO PFX "debugfs: Board not initialized.\n");
res = -EFAULT;
goto out_unlock;
}
if (sscanf(buf, "%lli", &tsf) != 1) {
printk(KERN_INFO PFX "debugfs: invalid values for \"tsf\"\n");
res = -EINVAL;
goto out_unlock;
}
bcm43xx_tsf_write(bcm, tsf);
mmiowb();
res = buf_size;
out_unlock:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
out_up:
up(&big_buffer_sem);
return res;
}
static ssize_t txstat_read_file(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const size_t len = REALLY_BIG_BUFFER_SIZE;
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
size_t pos = 0;
ssize_t res;
unsigned long flags;
struct bcm43xx_dfsentry *e;
struct bcm43xx_xmitstatus *status;
int i, cnt, j = 0;
down(&big_buffer_sem);
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
fappend("Last %d logged xmitstatus blobs (Latest first):\n\n",
BCM43xx_NR_LOGGED_XMITSTATUS);
e = bcm->dfsentry;
if (e->xmitstatus_printing == 0) {
/* At the beginning, make a copy of all data to avoid
* concurrency, as this function is called multiple
* times for big logs. Without copying, the data might
* change between reads. This would result in total trash.
*/
e->xmitstatus_printing = 1;
e->saved_xmitstatus_ptr = e->xmitstatus_ptr;
e->saved_xmitstatus_cnt = e->xmitstatus_cnt;
memcpy(e->xmitstatus_print_buffer, e->xmitstatus_buffer,
BCM43xx_NR_LOGGED_XMITSTATUS * sizeof(*(e->xmitstatus_buffer)));
}
i = e->saved_xmitstatus_ptr - 1;
if (i < 0)
i = BCM43xx_NR_LOGGED_XMITSTATUS - 1;
cnt = e->saved_xmitstatus_cnt;
while (cnt) {
status = e->xmitstatus_print_buffer + i;
fappend("0x%02x: cookie: 0x%04x, flags: 0x%02x, "
"cnt1: 0x%02x, cnt2: 0x%02x, seq: 0x%04x, "
"unk: 0x%04x\n", j,
status->cookie, status->flags,
status->cnt1, status->cnt2, status->seq,
status->unknown);
j++;
cnt--;
i--;
if (i < 0)
i = BCM43xx_NR_LOGGED_XMITSTATUS - 1;
}
spin_unlock_irqrestore(&bcm->irq_lock, flags);
res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (*ppos == pos) {
/* Done. Drop the copied data. */
e->xmitstatus_printing = 0;
}
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
up(&big_buffer_sem);
return res;
}
static ssize_t restart_write_file(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct bcm43xx_private *bcm = file->private_data;
char *buf = really_big_buffer;
ssize_t buf_size;
ssize_t res;
unsigned long flags;
buf_size = min(count, sizeof (really_big_buffer) - 1);
down(&big_buffer_sem);
if (copy_from_user(buf, user_buf, buf_size)) {
res = -EFAULT;
goto out_up;
}
mutex_lock(&(bcm)->mutex);
spin_lock_irqsave(&(bcm)->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
printk(KERN_INFO PFX "debugfs: Board not initialized.\n");
res = -EFAULT;
goto out_unlock;
}
if (count > 0 && buf[0] == '1') {
bcm43xx_controller_restart(bcm, "manually restarted");
res = count;
} else
res = -EINVAL;
out_unlock:
spin_unlock_irqrestore(&(bcm)->irq_lock, flags);
mutex_unlock(&(bcm)->mutex);
out_up:
up(&big_buffer_sem);
return res;
}
#undef fappend
static const struct file_operations devinfo_fops = {
.read = devinfo_read_file,
.write = write_file_dummy,
.open = open_file_generic,
};
static const struct file_operations spromdump_fops = {
.read = spromdump_read_file,
.write = write_file_dummy,
.open = open_file_generic,
};
static const struct file_operations drvinfo_fops = {
.read = drvinfo_read_file,
.write = write_file_dummy,
.open = open_file_generic,
};
static const struct file_operations tsf_fops = {
.read = tsf_read_file,
.write = tsf_write_file,
.open = open_file_generic,
};
static const struct file_operations txstat_fops = {
.read = txstat_read_file,
.write = write_file_dummy,
.open = open_file_generic,
};
static const struct file_operations restart_fops = {
.write = restart_write_file,
.open = open_file_generic,
};
void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)
{
struct bcm43xx_dfsentry *e;
char devdir[IFNAMSIZ];
assert(bcm);
e = kzalloc(sizeof(*e), GFP_KERNEL);
if (!e) {
printk(KERN_ERR PFX "out of memory\n");
return;
}
e->bcm = bcm;
e->xmitstatus_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS
* sizeof(*(e->xmitstatus_buffer)),
GFP_KERNEL);
if (!e->xmitstatus_buffer) {
printk(KERN_ERR PFX "out of memory\n");
kfree(e);
return;
}
e->xmitstatus_print_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS
* sizeof(*(e->xmitstatus_buffer)),
GFP_KERNEL);
if (!e->xmitstatus_print_buffer) {
printk(KERN_ERR PFX "out of memory\n");
kfree(e);
return;
}
bcm->dfsentry = e;
strncpy(devdir, bcm->net_dev->name, ARRAY_SIZE(devdir));
e->subdir = debugfs_create_dir(devdir, fs.root);
e->dentry_devinfo = debugfs_create_file("devinfo", 0444, e->subdir,
bcm, &devinfo_fops);
if (!e->dentry_devinfo)
printk(KERN_ERR PFX "debugfs: creating \"devinfo\" for \"%s\" failed!\n", devdir);
e->dentry_spromdump = debugfs_create_file("sprom_dump", 0444, e->subdir,
bcm, &spromdump_fops);
if (!e->dentry_spromdump)
printk(KERN_ERR PFX "debugfs: creating \"sprom_dump\" for \"%s\" failed!\n", devdir);
e->dentry_tsf = debugfs_create_file("tsf", 0666, e->subdir,
bcm, &tsf_fops);
if (!e->dentry_tsf)
printk(KERN_ERR PFX "debugfs: creating \"tsf\" for \"%s\" failed!\n", devdir);
e->dentry_txstat = debugfs_create_file("tx_status", 0444, e->subdir,
bcm, &txstat_fops);
if (!e->dentry_txstat)
printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir);
e->dentry_restart = debugfs_create_file("restart", 0222, e->subdir,
bcm, &restart_fops);
if (!e->dentry_restart)
printk(KERN_ERR PFX "debugfs: creating \"restart\" for \"%s\" failed!\n", devdir);
}
void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)
{
struct bcm43xx_dfsentry *e;
if (!bcm)
return;
e = bcm->dfsentry;
assert(e);
debugfs_remove(e->dentry_spromdump);
debugfs_remove(e->dentry_devinfo);
debugfs_remove(e->dentry_tsf);
debugfs_remove(e->dentry_txstat);
debugfs_remove(e->dentry_restart);
debugfs_remove(e->subdir);
kfree(e->xmitstatus_buffer);
kfree(e->xmitstatus_print_buffer);
kfree(e);
}
void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status)
{
struct bcm43xx_dfsentry *e;
struct bcm43xx_xmitstatus *savedstatus;
/* This is protected by bcm->_lock */
e = bcm->dfsentry;
assert(e);
savedstatus = e->xmitstatus_buffer + e->xmitstatus_ptr;
memcpy(savedstatus, status, sizeof(*status));
e->xmitstatus_ptr++;
if (e->xmitstatus_ptr >= BCM43xx_NR_LOGGED_XMITSTATUS)
e->xmitstatus_ptr = 0;
if (e->xmitstatus_cnt < BCM43xx_NR_LOGGED_XMITSTATUS)
e->xmitstatus_cnt++;
}
void bcm43xx_debugfs_init(void)
{
memset(&fs, 0, sizeof(fs));
fs.root = debugfs_create_dir(KBUILD_MODNAME, NULL);
if (!fs.root)
printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "\" subdir failed!\n");
fs.dentry_driverinfo = debugfs_create_file("driver", 0444, fs.root, NULL, &drvinfo_fops);
if (!fs.dentry_driverinfo)
printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "/driver\" failed!\n");
}
void bcm43xx_debugfs_exit(void)
{
debugfs_remove(fs.dentry_driverinfo);
debugfs_remove(fs.root);
}
void bcm43xx_printk_dump(const char *data,
size_t size,
const char *description)
{
size_t i;
char c;
printk(KERN_INFO PFX "Data dump (%s, %zd bytes):",
description, size);
for (i = 0; i < size; i++) {
c = data[i];
if (i % 8 == 0)
printk("\n" KERN_INFO PFX "0x%08zx: 0x%02x, ", i, c & 0xff);
else
printk("0x%02x, ", c & 0xff);
}
printk("\n");
}
void bcm43xx_printk_bitdump(const unsigned char *data,
size_t bytes, int msb_to_lsb,
const char *description)
{
size_t i;
int j;
const unsigned char *d;
printk(KERN_INFO PFX "*** Bitdump (%s, %zd bytes, %s) ***",
description, bytes, msb_to_lsb ? "MSB to LSB" : "LSB to MSB");
for (i = 0; i < bytes; i++) {
d = data + i;
if (i % 8 == 0)
printk("\n" KERN_INFO PFX "0x%08zx: ", i);
if (msb_to_lsb) {
for (j = 7; j >= 0; j--) {
if (*d & (1 << j))
printk("1");
else
printk("0");
}
} else {
for (j = 0; j < 8; j++) {
if (*d & (1 << j))
printk("1");
else
printk("0");
}
}
printk(" ");
}
printk("\n");
}
#ifndef BCM43xx_DEBUGFS_H_
#define BCM43xx_DEBUGFS_H_
struct bcm43xx_private;
struct bcm43xx_xmitstatus;
#ifdef CONFIG_BCM43XX_DEBUG
#include <linux/list.h>
#include <asm/semaphore.h>
struct dentry;
/* limited by the size of the "really_big_buffer" */
#define BCM43xx_NR_LOGGED_XMITSTATUS 100
struct bcm43xx_dfsentry {
struct dentry *subdir;
struct dentry *dentry_devinfo;
struct dentry *dentry_spromdump;
struct dentry *dentry_tsf;
struct dentry *dentry_txstat;
struct dentry *dentry_restart;
struct bcm43xx_private *bcm;
/* saved xmitstatus. */
struct bcm43xx_xmitstatus *xmitstatus_buffer;
int xmitstatus_ptr;
int xmitstatus_cnt;
/* We need a seperate buffer while printing to avoid
* concurrency issues. (New xmitstatus can arrive
* while we are printing).
*/
struct bcm43xx_xmitstatus *xmitstatus_print_buffer;
int saved_xmitstatus_ptr;
int saved_xmitstatus_cnt;
int xmitstatus_printing;
};
struct bcm43xx_debugfs {
struct dentry *root;
struct dentry *dentry_driverinfo;
};
void bcm43xx_debugfs_init(void);
void bcm43xx_debugfs_exit(void);
void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm);
void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm);
void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status);
/* Debug helper: Dump binary data through printk. */
void bcm43xx_printk_dump(const char *data,
size_t size,
const char *description);
/* Debug helper: Dump bitwise binary data through printk. */
void bcm43xx_printk_bitdump(const unsigned char *data,
size_t bytes, int msb_to_lsb,
const char *description);
#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) \
do { \
bcm43xx_printk_bitdump((const unsigned char *)(pointer), \
sizeof(*(pointer)), \
(msb_to_lsb), \
(description)); \
} while (0)
#else /* CONFIG_BCM43XX_DEBUG*/
static inline
void bcm43xx_debugfs_init(void) { }
static inline
void bcm43xx_debugfs_exit(void) { }
static inline
void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) { }
static inline
void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) { }
static inline
void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status) { }
static inline
void bcm43xx_printk_dump(const char *data,
size_t size,
const char *description)
{
}
static inline
void bcm43xx_printk_bitdump(const unsigned char *data,
size_t bytes, int msb_to_lsb,
const char *description)
{
}
#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) do { /* nothing */ } while (0)
#endif /* CONFIG_BCM43XX_DEBUG*/
/* Ugly helper macros to make incomplete code more verbose on runtime */
#ifdef TODO
# undef TODO
#endif
#define TODO() \
do { \
printk(KERN_INFO PFX "TODO: Incomplete code in %s() at %s:%d\n", \
__FUNCTION__, __FILE__, __LINE__); \
} while (0)
#ifdef FIXME
# undef FIXME
#endif
#define FIXME() \
do { \
printk(KERN_INFO PFX "FIXME: Possibly broken code in %s() at %s:%d\n", \
__FUNCTION__, __FILE__, __LINE__); \
} while (0)
#endif /* BCM43xx_DEBUGFS_H_ */
/*
Broadcom BCM43xx wireless driver
DMA ringbuffer and descriptor allocation/management
Copyright (c) 2005, 2006 Michael Buesch <mbuesch@freenet.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 "bcm43xx.h"
#include "bcm43xx_dma.h"
#include "bcm43xx_main.h"
#include "bcm43xx_debugfs.h"
#include "bcm43xx_power.h"
#include "bcm43xx_xmit.h"
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
static inline int free_slots(struct bcm43xx_dmaring *ring)
{
return (ring->nr_slots - ring->used_slots);
}
static inline int next_slot(struct bcm43xx_dmaring *ring, int slot)
{
assert(slot >= -1 && slot <= ring->nr_slots - 1);
if (slot == ring->nr_slots - 1)
return 0;
return slot + 1;
}
static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot)
{
assert(slot >= 0 && slot <= ring->nr_slots - 1);
if (slot == 0)
return ring->nr_slots - 1;
return slot - 1;
}
/* Request a slot for usage. */
static inline
int request_slot(struct bcm43xx_dmaring *ring)
{
int slot;
assert(ring->tx);
assert(!ring->suspended);
assert(free_slots(ring) != 0);
slot = next_slot(ring, ring->current_slot);
ring->current_slot = slot;
ring->used_slots++;
/* Check the number of available slots and suspend TX,
* if we are running low on free slots.
*/
if (unlikely(free_slots(ring) < ring->suspend_mark)) {
netif_stop_queue(ring->bcm->net_dev);
ring->suspended = 1;
}
#ifdef CONFIG_BCM43XX_DEBUG
if (ring->used_slots > ring->max_used_slots)
ring->max_used_slots = ring->used_slots;
#endif /* CONFIG_BCM43XX_DEBUG*/
return slot;
}
/* Return a slot to the free slots. */
static inline
void return_slot(struct bcm43xx_dmaring *ring, int slot)
{
assert(ring->tx);
ring->used_slots--;
/* Check if TX is suspended and check if we have
* enough free slots to resume it again.
*/
if (unlikely(ring->suspended)) {
if (free_slots(ring) >= ring->resume_mark) {
ring->suspended = 0;
netif_wake_queue(ring->bcm->net_dev);
}
}
}
u16 bcm43xx_dmacontroller_base(int dma64bit, int controller_idx)
{
static const u16 map64[] = {
BCM43xx_MMIO_DMA64_BASE0,
BCM43xx_MMIO_DMA64_BASE1,
BCM43xx_MMIO_DMA64_BASE2,
BCM43xx_MMIO_DMA64_BASE3,
BCM43xx_MMIO_DMA64_BASE4,
BCM43xx_MMIO_DMA64_BASE5,
};
static const u16 map32[] = {
BCM43xx_MMIO_DMA32_BASE0,
BCM43xx_MMIO_DMA32_BASE1,
BCM43xx_MMIO_DMA32_BASE2,
BCM43xx_MMIO_DMA32_BASE3,
BCM43xx_MMIO_DMA32_BASE4,
BCM43xx_MMIO_DMA32_BASE5,
};
if (dma64bit) {
assert(controller_idx >= 0 &&
controller_idx < ARRAY_SIZE(map64));
return map64[controller_idx];
}
assert(controller_idx >= 0 &&
controller_idx < ARRAY_SIZE(map32));
return map32[controller_idx];
}
static inline
dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring,
unsigned char *buf,
size_t len,
int tx)
{
dma_addr_t dmaaddr;
int direction = PCI_DMA_FROMDEVICE;
if (tx)
direction = PCI_DMA_TODEVICE;
dmaaddr = pci_map_single(ring->bcm->pci_dev,
buf, len,
direction);
return dmaaddr;
}
static inline
void unmap_descbuffer(struct bcm43xx_dmaring *ring,
dma_addr_t addr,
size_t len,
int tx)
{
if (tx) {
pci_unmap_single(ring->bcm->pci_dev,
addr, len,
PCI_DMA_TODEVICE);
} else {
pci_unmap_single(ring->bcm->pci_dev,
addr, len,
PCI_DMA_FROMDEVICE);
}
}
static inline
void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring,
dma_addr_t addr,
size_t len)
{
assert(!ring->tx);
pci_dma_sync_single_for_cpu(ring->bcm->pci_dev,
addr, len, PCI_DMA_FROMDEVICE);
}
static inline
void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring,
dma_addr_t addr,
size_t len)
{
assert(!ring->tx);
pci_dma_sync_single_for_cpu(ring->bcm->pci_dev,
addr, len, PCI_DMA_TODEVICE);
}
/* Unmap and free a descriptor buffer. */
static inline
void free_descriptor_buffer(struct bcm43xx_dmaring *ring,
struct bcm43xx_dmadesc_meta *meta,
int irq_context)
{
assert(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 bcm43xx_dmaring *ring)
{
ring->descbase = pci_alloc_consistent(ring->bcm->pci_dev, BCM43xx_DMA_RINGMEMSIZE,
&(ring->dmabase));
if (!ring->descbase) {
/* Allocation may have failed due to pci_alloc_consistent
insisting on use of GFP_DMA, which is more restrictive
than necessary... */
struct dma_desc *rx_ring;
dma_addr_t rx_ring_dma;
rx_ring = kzalloc(BCM43xx_DMA_RINGMEMSIZE, GFP_KERNEL);
if (!rx_ring)
goto out_err;
rx_ring_dma = pci_map_single(ring->bcm->pci_dev, rx_ring,
BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(rx_ring_dma) ||
rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) {
/* Sigh... */
if (!pci_dma_mapping_error(rx_ring_dma))
pci_unmap_single(ring->bcm->pci_dev,
rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
rx_ring_dma = pci_map_single(ring->bcm->pci_dev,
rx_ring, BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(rx_ring_dma) ||
rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) {
assert(0);
if (!pci_dma_mapping_error(rx_ring_dma))
pci_unmap_single(ring->bcm->pci_dev,
rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
goto out_err;
}
}
ring->descbase = rx_ring;
ring->dmabase = rx_ring_dma;
}
memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE);
return 0;
out_err:
printk(KERN_ERR PFX "DMA ringmemory allocation failed\n");
return -ENOMEM;
}
static void free_ringmemory(struct bcm43xx_dmaring *ring)
{
struct device *dev = &(ring->bcm->pci_dev->dev);
dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,
ring->descbase, ring->dmabase);
}
/* Reset the RX DMA channel */
int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,
u16 mmio_base, int dma64)
{
int i;
u32 value;
u16 offset;
offset = dma64 ? BCM43xx_DMA64_RXCTL : BCM43xx_DMA32_RXCTL;
bcm43xx_write32(bcm, mmio_base + offset, 0);
for (i = 0; i < 1000; i++) {
offset = dma64 ? BCM43xx_DMA64_RXSTATUS : BCM43xx_DMA32_RXSTATUS;
value = bcm43xx_read32(bcm, mmio_base + offset);
if (dma64) {
value &= BCM43xx_DMA64_RXSTAT;
if (value == BCM43xx_DMA64_RXSTAT_DISABLED) {
i = -1;
break;
}
} else {
value &= BCM43xx_DMA32_RXSTATE;
if (value == BCM43xx_DMA32_RXSTAT_DISABLED) {
i = -1;
break;
}
}
udelay(10);
}
if (i != -1) {
printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n");
return -ENODEV;
}
return 0;
}
/* Reset the RX DMA channel */
int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,
u16 mmio_base, int dma64)
{
int i;
u32 value;
u16 offset;
for (i = 0; i < 1000; i++) {
offset = dma64 ? BCM43xx_DMA64_TXSTATUS : BCM43xx_DMA32_TXSTATUS;
value = bcm43xx_read32(bcm, mmio_base + offset);
if (dma64) {
value &= BCM43xx_DMA64_TXSTAT;
if (value == BCM43xx_DMA64_TXSTAT_DISABLED ||
value == BCM43xx_DMA64_TXSTAT_IDLEWAIT ||
value == BCM43xx_DMA64_TXSTAT_STOPPED)
break;
} else {
value &= BCM43xx_DMA32_TXSTATE;
if (value == BCM43xx_DMA32_TXSTAT_DISABLED ||
value == BCM43xx_DMA32_TXSTAT_IDLEWAIT ||
value == BCM43xx_DMA32_TXSTAT_STOPPED)
break;
}
udelay(10);
}
offset = dma64 ? BCM43xx_DMA64_TXCTL : BCM43xx_DMA32_TXCTL;
bcm43xx_write32(bcm, mmio_base + offset, 0);
for (i = 0; i < 1000; i++) {
offset = dma64 ? BCM43xx_DMA64_TXSTATUS : BCM43xx_DMA32_TXSTATUS;
value = bcm43xx_read32(bcm, mmio_base + offset);
if (dma64) {
value &= BCM43xx_DMA64_TXSTAT;
if (value == BCM43xx_DMA64_TXSTAT_DISABLED) {
i = -1;
break;
}
} else {
value &= BCM43xx_DMA32_TXSTATE;
if (value == BCM43xx_DMA32_TXSTAT_DISABLED) {
i = -1;
break;
}
}
udelay(10);
}
if (i != -1) {
printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n");
return -ENODEV;
}
/* ensure the reset is completed. */
udelay(300);
return 0;
}
static void fill_descriptor(struct bcm43xx_dmaring *ring,
struct bcm43xx_dmadesc_generic *desc,
dma_addr_t dmaaddr,
u16 bufsize,
int start, int end, int irq)
{
int slot;
slot = bcm43xx_dma_desc2idx(ring, desc);
assert(slot >= 0 && slot < ring->nr_slots);
if (ring->dma64) {
u32 ctl0 = 0, ctl1 = 0;
u32 addrlo, addrhi;
u32 addrext;
addrlo = (u32)(dmaaddr & 0xFFFFFFFF);
addrhi = (((u64)dmaaddr >> 32) & ~BCM43xx_DMA64_ROUTING);
addrext = (((u64)dmaaddr >> 32) >> BCM43xx_DMA64_ROUTING_SHIFT);
addrhi |= ring->routing;
if (slot == ring->nr_slots - 1)
ctl0 |= BCM43xx_DMA64_DCTL0_DTABLEEND;
if (start)
ctl0 |= BCM43xx_DMA64_DCTL0_FRAMESTART;
if (end)
ctl0 |= BCM43xx_DMA64_DCTL0_FRAMEEND;
if (irq)
ctl0 |= BCM43xx_DMA64_DCTL0_IRQ;
ctl1 |= (bufsize - ring->frameoffset)
& BCM43xx_DMA64_DCTL1_BYTECNT;
ctl1 |= (addrext << BCM43xx_DMA64_DCTL1_ADDREXT_SHIFT)
& BCM43xx_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);
} else {
u32 ctl;
u32 addr;
u32 addrext;
addr = (u32)(dmaaddr & ~BCM43xx_DMA32_ROUTING);
addrext = (u32)(dmaaddr & BCM43xx_DMA32_ROUTING)
>> BCM43xx_DMA32_ROUTING_SHIFT;
addr |= ring->routing;
ctl = (bufsize - ring->frameoffset)
& BCM43xx_DMA32_DCTL_BYTECNT;
if (slot == ring->nr_slots - 1)
ctl |= BCM43xx_DMA32_DCTL_DTABLEEND;
if (start)
ctl |= BCM43xx_DMA32_DCTL_FRAMESTART;
if (end)
ctl |= BCM43xx_DMA32_DCTL_FRAMEEND;
if (irq)
ctl |= BCM43xx_DMA32_DCTL_IRQ;
ctl |= (addrext << BCM43xx_DMA32_DCTL_ADDREXT_SHIFT)
& BCM43xx_DMA32_DCTL_ADDREXT_MASK;
desc->dma32.control = cpu_to_le32(ctl);
desc->dma32.address = cpu_to_le32(addr);
}
}
static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring,
struct bcm43xx_dmadesc_generic *desc,
struct bcm43xx_dmadesc_meta *meta,
gfp_t gfp_flags)
{
struct bcm43xx_rxhdr *rxhdr;
struct bcm43xx_hwxmitstatus *xmitstat;
dma_addr_t dmaaddr;
struct sk_buff *skb;
assert(!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);
/* This hardware bug work-around adapted from the b44 driver.
The chip may be unable to do PCI DMA to/from anything above 1GB */
if (pci_dma_mapping_error(dmaaddr) ||
dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) {
/* This one has 30-bit addressing... */
if (!pci_dma_mapping_error(dmaaddr))
pci_unmap_single(ring->bcm->pci_dev,
dmaaddr, ring->rx_buffersize,
PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
skb = __dev_alloc_skb(ring->rx_buffersize,GFP_DMA);
if (skb == NULL)
return -ENOMEM;
dmaaddr = pci_map_single(ring->bcm->pci_dev,
skb->data, ring->rx_buffersize,
PCI_DMA_FROMDEVICE);
if (pci_dma_mapping_error(dmaaddr) ||
dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) {
assert(0);
dev_kfree_skb_any(skb);
return -ENOMEM;
}
}
meta->skb = skb;
meta->dmaaddr = dmaaddr;
skb->dev = ring->bcm->net_dev;
fill_descriptor(ring, desc, dmaaddr,
ring->rx_buffersize, 0, 0, 0);
rxhdr = (struct bcm43xx_rxhdr *)(skb->data);
rxhdr->frame_length = 0;
rxhdr->flags1 = 0;
xmitstat = (struct bcm43xx_hwxmitstatus *)(skb->data);
xmitstat->cookie = 0;
return 0;
}
/* Allocate the initial descbuffers.
* This is used for an RX ring only.
*/
static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring)
{
int i, err = -ENOMEM;
struct bcm43xx_dmadesc_generic *desc;
struct bcm43xx_dmadesc_meta *meta;
for (i = 0; i < ring->nr_slots; i++) {
desc = bcm43xx_dma_idx2desc(ring, i, &meta);
err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL);
if (err)
goto err_unwind;
}
mb();
ring->used_slots = ring->nr_slots;
err = 0;
out:
return err;
err_unwind:
for (i--; i >= 0; i--) {
desc = bcm43xx_dma_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 bcm43xx_dmaring *ring)
{
int err = 0;
u32 value;
u32 addrext;
if (ring->tx) {
if (ring->dma64) {
u64 ringbase = (u64)(ring->dmabase);
addrext = ((ringbase >> 32) >> BCM43xx_DMA64_ROUTING_SHIFT);
value = BCM43xx_DMA64_TXENABLE;
value |= (addrext << BCM43xx_DMA64_TXADDREXT_SHIFT)
& BCM43xx_DMA64_TXADDREXT_MASK;
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, value);
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGLO,
(ringbase & 0xFFFFFFFF));
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGHI,
((ringbase >> 32) & ~BCM43xx_DMA64_ROUTING)
| ring->routing);
} else {
u32 ringbase = (u32)(ring->dmabase);
addrext = (ringbase >> BCM43xx_DMA32_ROUTING_SHIFT);
value = BCM43xx_DMA32_TXENABLE;
value |= (addrext << BCM43xx_DMA32_TXADDREXT_SHIFT)
& BCM43xx_DMA32_TXADDREXT_MASK;
bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, value);
bcm43xx_dma_write(ring, BCM43xx_DMA32_TXRING,
(ringbase & ~BCM43xx_DMA32_ROUTING)
| ring->routing);
}
} else {
err = alloc_initial_descbuffers(ring);
if (err)
goto out;
if (ring->dma64) {
u64 ringbase = (u64)(ring->dmabase);
addrext = ((ringbase >> 32) >> BCM43xx_DMA64_ROUTING_SHIFT);
value = (ring->frameoffset << BCM43xx_DMA64_RXFROFF_SHIFT);
value |= BCM43xx_DMA64_RXENABLE;
value |= (addrext << BCM43xx_DMA64_RXADDREXT_SHIFT)
& BCM43xx_DMA64_RXADDREXT_MASK;
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXCTL, value);
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGLO,
(ringbase & 0xFFFFFFFF));
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGHI,
((ringbase >> 32) & ~BCM43xx_DMA64_ROUTING)
| ring->routing);
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXINDEX, 200);
} else {
u32 ringbase = (u32)(ring->dmabase);
addrext = (ringbase >> BCM43xx_DMA32_ROUTING_SHIFT);
value = (ring->frameoffset << BCM43xx_DMA32_RXFROFF_SHIFT);
value |= BCM43xx_DMA32_RXENABLE;
value |= (addrext << BCM43xx_DMA32_RXADDREXT_SHIFT)
& BCM43xx_DMA32_RXADDREXT_MASK;
bcm43xx_dma_write(ring, BCM43xx_DMA32_RXCTL, value);
bcm43xx_dma_write(ring, BCM43xx_DMA32_RXRING,
(ringbase & ~BCM43xx_DMA32_ROUTING)
| ring->routing);
bcm43xx_dma_write(ring, BCM43xx_DMA32_RXINDEX, 200);
}
}
out:
return err;
}
/* Shutdown the DMA controller. */
static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring)
{
if (ring->tx) {
bcm43xx_dmacontroller_tx_reset(ring->bcm, ring->mmio_base, ring->dma64);
if (ring->dma64) {
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGLO, 0);
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGHI, 0);
} else
bcm43xx_dma_write(ring, BCM43xx_DMA32_TXRING, 0);
} else {
bcm43xx_dmacontroller_rx_reset(ring->bcm, ring->mmio_base, ring->dma64);
if (ring->dma64) {
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGLO, 0);
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGHI, 0);
} else
bcm43xx_dma_write(ring, BCM43xx_DMA32_RXRING, 0);
}
}
static void free_all_descbuffers(struct bcm43xx_dmaring *ring)
{
struct bcm43xx_dmadesc_generic *desc;
struct bcm43xx_dmadesc_meta *meta;
int i;
if (!ring->used_slots)
return;
for (i = 0; i < ring->nr_slots; i++) {
desc = bcm43xx_dma_idx2desc(ring, i, &meta);
if (!meta->skb) {
assert(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);
}
}
/* Main initialization function. */
static
struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm,
int controller_index,
int for_tx,
int dma64)
{
struct bcm43xx_dmaring *ring;
int err;
int nr_slots;
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
if (!ring)
goto out;
nr_slots = BCM43xx_RXRING_SLOTS;
if (for_tx)
nr_slots = BCM43xx_TXRING_SLOTS;
ring->meta = kcalloc(nr_slots, sizeof(struct bcm43xx_dmadesc_meta),
GFP_KERNEL);
if (!ring->meta)
goto err_kfree_ring;
ring->routing = BCM43xx_DMA32_CLIENTTRANS;
if (dma64)
ring->routing = BCM43xx_DMA64_CLIENTTRANS;
ring->bcm = bcm;
ring->nr_slots = nr_slots;
ring->suspend_mark = ring->nr_slots * BCM43xx_TXSUSPEND_PERCENT / 100;
ring->resume_mark = ring->nr_slots * BCM43xx_TXRESUME_PERCENT / 100;
assert(ring->suspend_mark < ring->resume_mark);
ring->mmio_base = bcm43xx_dmacontroller_base(dma64, controller_index);
ring->index = controller_index;
ring->dma64 = !!dma64;
if (for_tx) {
ring->tx = 1;
ring->current_slot = -1;
} else {
if (ring->index == 0) {
ring->rx_buffersize = BCM43xx_DMA0_RX_BUFFERSIZE;
ring->frameoffset = BCM43xx_DMA0_RX_FRAMEOFFSET;
} else if (ring->index == 3) {
ring->rx_buffersize = BCM43xx_DMA3_RX_BUFFERSIZE;
ring->frameoffset = BCM43xx_DMA3_RX_FRAMEOFFSET;
} else
assert(0);
}
err = alloc_ringmemory(ring);
if (err)
goto err_kfree_meta;
err = dmacontroller_setup(ring);
if (err)
goto err_free_ringmemory;
return ring;
out:
printk(KERN_ERR PFX "Error in bcm43xx_setup_dmaring\n");
return ring;
err_free_ringmemory:
free_ringmemory(ring);
err_kfree_meta:
kfree(ring->meta);
err_kfree_ring:
kfree(ring);
ring = NULL;
goto out;
}
/* Main cleanup function. */
static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring)
{
if (!ring)
return;
dprintk(KERN_INFO PFX "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->meta);
kfree(ring);
}
void bcm43xx_dma_free(struct bcm43xx_private *bcm)
{
struct bcm43xx_dma *dma;
if (bcm43xx_using_pio(bcm))
return;
dma = bcm43xx_current_dma(bcm);
bcm43xx_destroy_dmaring(dma->rx_ring3);
dma->rx_ring3 = NULL;
bcm43xx_destroy_dmaring(dma->rx_ring0);
dma->rx_ring0 = NULL;
bcm43xx_destroy_dmaring(dma->tx_ring5);
dma->tx_ring5 = NULL;
bcm43xx_destroy_dmaring(dma->tx_ring4);
dma->tx_ring4 = NULL;
bcm43xx_destroy_dmaring(dma->tx_ring3);
dma->tx_ring3 = NULL;
bcm43xx_destroy_dmaring(dma->tx_ring2);
dma->tx_ring2 = NULL;
bcm43xx_destroy_dmaring(dma->tx_ring1);
dma->tx_ring1 = NULL;
bcm43xx_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL;
}
int bcm43xx_dma_init(struct bcm43xx_private *bcm)
{
struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm);
struct bcm43xx_dmaring *ring;
int err = -ENOMEM;
int dma64 = 0;
bcm->dma_mask = bcm43xx_get_supported_dma_mask(bcm);
if (bcm->dma_mask == DMA_64BIT_MASK)
dma64 = 1;
err = pci_set_dma_mask(bcm->pci_dev, bcm->dma_mask);
if (err)
goto no_dma;
err = pci_set_consistent_dma_mask(bcm->pci_dev, bcm->dma_mask);
if (err)
goto no_dma;
/* setup TX DMA channels. */
ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64);
if (!ring)
goto out;
dma->tx_ring0 = ring;
ring = bcm43xx_setup_dmaring(bcm, 1, 1, dma64);
if (!ring)
goto err_destroy_tx0;
dma->tx_ring1 = ring;
ring = bcm43xx_setup_dmaring(bcm, 2, 1, dma64);
if (!ring)
goto err_destroy_tx1;
dma->tx_ring2 = ring;
ring = bcm43xx_setup_dmaring(bcm, 3, 1, dma64);
if (!ring)
goto err_destroy_tx2;
dma->tx_ring3 = ring;
ring = bcm43xx_setup_dmaring(bcm, 4, 1, dma64);
if (!ring)
goto err_destroy_tx3;
dma->tx_ring4 = ring;
ring = bcm43xx_setup_dmaring(bcm, 5, 1, dma64);
if (!ring)
goto err_destroy_tx4;
dma->tx_ring5 = ring;
/* setup RX DMA channels. */
ring = bcm43xx_setup_dmaring(bcm, 0, 0, dma64);
if (!ring)
goto err_destroy_tx5;
dma->rx_ring0 = ring;
if (bcm->current_core->rev < 5) {
ring = bcm43xx_setup_dmaring(bcm, 3, 0, dma64);
if (!ring)
goto err_destroy_rx0;
dma->rx_ring3 = ring;
}
dprintk(KERN_INFO PFX "%d-bit DMA initialized\n",
(bcm->dma_mask == DMA_64BIT_MASK) ? 64 :
(bcm->dma_mask == DMA_32BIT_MASK) ? 32 : 30);
err = 0;
out:
return err;
err_destroy_rx0:
bcm43xx_destroy_dmaring(dma->rx_ring0);
dma->rx_ring0 = NULL;
err_destroy_tx5:
bcm43xx_destroy_dmaring(dma->tx_ring5);
dma->tx_ring5 = NULL;
err_destroy_tx4:
bcm43xx_destroy_dmaring(dma->tx_ring4);
dma->tx_ring4 = NULL;
err_destroy_tx3:
bcm43xx_destroy_dmaring(dma->tx_ring3);
dma->tx_ring3 = NULL;
err_destroy_tx2:
bcm43xx_destroy_dmaring(dma->tx_ring2);
dma->tx_ring2 = NULL;
err_destroy_tx1:
bcm43xx_destroy_dmaring(dma->tx_ring1);
dma->tx_ring1 = NULL;
err_destroy_tx0:
bcm43xx_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL;
no_dma:
#ifdef CONFIG_BCM43XX_PIO
printk(KERN_WARNING PFX "DMA not supported on this device."
" Falling back to PIO.\n");
bcm->__using_pio = 1;
return -ENOSYS;
#else
printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. "
"Please recompile the driver with PIO support.\n");
return -ENODEV;
#endif /* CONFIG_BCM43XX_PIO */
}
/* Generate a cookie for the TX header. */
static u16 generate_cookie(struct bcm43xx_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;
}
assert(((u16)slot & 0xF000) == 0x0000);
cookie |= (u16)slot;
return cookie;
}
/* Inspect a cookie and find out to which controller/slot it belongs. */
static
struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm,
u16 cookie, int *slot)
{
struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm);
struct bcm43xx_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:
assert(0);
}
*slot = (cookie & 0x0FFF);
assert(*slot >= 0 && *slot < ring->nr_slots);
return ring;
}
static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring,
int slot)
{
u16 offset;
int descsize;
/* Everything is ready to start. Buffers are DMA mapped and
* associated with slots.
* "slot" is the last slot of the new frame we want to transmit.
* Close your seat belts now, please.
*/
wmb();
slot = next_slot(ring, slot);
offset = (ring->dma64) ? BCM43xx_DMA64_TXINDEX : BCM43xx_DMA32_TXINDEX;
descsize = (ring->dma64) ? sizeof(struct bcm43xx_dmadesc64)
: sizeof(struct bcm43xx_dmadesc32);
bcm43xx_dma_write(ring, offset,
(u32)(slot * descsize));
}
static void dma_tx_fragment(struct bcm43xx_dmaring *ring,
struct sk_buff *skb,
u8 cur_frag)
{
int slot;
struct bcm43xx_dmadesc_generic *desc;
struct bcm43xx_dmadesc_meta *meta;
dma_addr_t dmaaddr;
struct sk_buff *bounce_skb;
assert(skb_shinfo(skb)->nr_frags == 0);
slot = request_slot(ring);
desc = bcm43xx_dma_idx2desc(ring, slot, &meta);
/* Add a device specific TX header. */
assert(skb_headroom(skb) >= sizeof(struct bcm43xx_txhdr));
/* Reserve enough headroom for the device tx header. */
__skb_push(skb, sizeof(struct bcm43xx_txhdr));
/* Now calculate and add the tx header.
* The tx header includes the PLCP header.
*/
bcm43xx_generate_txhdr(ring->bcm,
(struct bcm43xx_txhdr *)skb->data,
skb->data + sizeof(struct bcm43xx_txhdr),
skb->len - sizeof(struct bcm43xx_txhdr),
(cur_frag == 0),
generate_cookie(ring, slot));
dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) {
/* chip cannot handle DMA to/from > 1GB, use bounce buffer (copied from b44 driver) */
if (!dma_mapping_error(dmaaddr))
unmap_descbuffer(ring, dmaaddr, skb->len, 1);
bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC|GFP_DMA);
if (!bounce_skb)
return;
dmaaddr = map_descbuffer(ring, bounce_skb->data, bounce_skb->len, 1);
if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) {
if (!dma_mapping_error(dmaaddr))
unmap_descbuffer(ring, dmaaddr, skb->len, 1);
dev_kfree_skb_any(bounce_skb);
assert(0);
return;
}
skb_copy_from_linear_data(skb, skb_put(bounce_skb, skb->len),
skb->len);
dev_kfree_skb_any(skb);
skb = bounce_skb;
}
meta->skb = skb;
meta->dmaaddr = dmaaddr;
fill_descriptor(ring, desc, dmaaddr,
skb->len, 1, 1, 1);
/* Now transfer the whole frame. */
dmacontroller_poke_tx(ring, slot);
}
int bcm43xx_dma_tx(struct bcm43xx_private *bcm,
struct ieee80211_txb *txb)
{
/* We just received a packet from the kernel network subsystem.
* Add headers and DMA map the memory. Poke
* the device to send the stuff.
* Note that this is called from atomic context.
*/
struct bcm43xx_dmaring *ring = bcm43xx_current_dma(bcm)->tx_ring1;
u8 i;
struct sk_buff *skb;
assert(ring->tx);
if (unlikely(free_slots(ring) < txb->nr_frags)) {
/* The queue should be stopped,
* if we are low on free slots.
* If this ever triggers, we have to lower the suspend_mark.
*/
dprintkl(KERN_ERR PFX "Out of DMA descriptor slots!\n");
return -ENOMEM;
}
for (i = 0; i < txb->nr_frags; i++) {
skb = txb->fragments[i];
/* Take skb from ieee80211_txb_free */
txb->fragments[i] = NULL;
dma_tx_fragment(ring, skb, i);
}
ieee80211_txb_free(txb);
return 0;
}
void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status)
{
struct bcm43xx_dmaring *ring;
struct bcm43xx_dmadesc_generic *desc;
struct bcm43xx_dmadesc_meta *meta;
int is_last_fragment;
int slot;
u32 tmp;
ring = parse_cookie(bcm, status->cookie, &slot);
assert(ring);
assert(ring->tx);
while (1) {
assert(slot >= 0 && slot < ring->nr_slots);
desc = bcm43xx_dma_idx2desc(ring, slot, &meta);
if (ring->dma64) {
tmp = le32_to_cpu(desc->dma64.control0);
is_last_fragment = !!(tmp & BCM43xx_DMA64_DCTL0_FRAMEEND);
} else {
tmp = le32_to_cpu(desc->dma32.control);
is_last_fragment = !!(tmp & BCM43xx_DMA32_DCTL_FRAMEEND);
}
unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);
free_descriptor_buffer(ring, meta, 1);
/* Everything belonging to the slot is unmapped
* and freed, so we can return it.
*/
return_slot(ring, slot);
if (is_last_fragment)
break;
slot = next_slot(ring, slot);
}
bcm->stats.last_tx = jiffies;
}
static void dma_rx(struct bcm43xx_dmaring *ring,
int *slot)
{
struct bcm43xx_dmadesc_generic *desc;
struct bcm43xx_dmadesc_meta *meta;
struct bcm43xx_rxhdr *rxhdr;
struct sk_buff *skb;
u16 len;
int err;
dma_addr_t dmaaddr;
desc = bcm43xx_dma_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 bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data;
struct bcm43xx_xmitstatus stat;
int i = 0;
stat.cookie = le16_to_cpu(hw->cookie);
while (stat.cookie == 0) {
if (unlikely(++i >= 10000)) {
assert(0);
break;
}
udelay(2);
barrier();
stat.cookie = le16_to_cpu(hw->cookie);
}
stat.flags = hw->flags;
stat.cnt1 = hw->cnt1;
stat.cnt2 = hw->cnt2;
stat.seq = le16_to_cpu(hw->seq);
stat.unknown = le16_to_cpu(hw->unknown);
bcm43xx_debugfs_log_txstat(ring->bcm, &stat);
bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat);
/* recycle the descriptor buffer. */
sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize);
return;
}
rxhdr = (struct bcm43xx_rxhdr *)skb->data;
len = le16_to_cpu(rxhdr->frame_length);
if (len == 0) {
int i = 0;
do {
udelay(2);
barrier();
len = le16_to_cpu(rxhdr->frame_length);
} 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 = bcm43xx_dma_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;
}
printkl(KERN_ERR PFX "DMA RX buffer too small "
"(len: %u, buffer: %u, nr-dropped: %d)\n",
len, ring->rx_buffersize, cnt);
goto drop;
}
len -= IEEE80211_FCS_LEN;
dmaaddr = meta->dmaaddr;
err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC);
if (unlikely(err)) {
dprintkl(KERN_ERR PFX "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);
err = bcm43xx_rx(ring->bcm, skb, rxhdr);
if (err) {
dev_kfree_skb_irq(skb);
goto drop;
}
drop:
return;
}
void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)
{
u32 status;
u16 descptr;
int slot, current_slot;
#ifdef CONFIG_BCM43XX_DEBUG
int used_slots = 0;
#endif
assert(!ring->tx);
if (ring->dma64) {
status = bcm43xx_dma_read(ring, BCM43xx_DMA64_RXSTATUS);
descptr = (status & BCM43xx_DMA64_RXSTATDPTR);
current_slot = descptr / sizeof(struct bcm43xx_dmadesc64);
} else {
status = bcm43xx_dma_read(ring, BCM43xx_DMA32_RXSTATUS);
descptr = (status & BCM43xx_DMA32_RXDPTR);
current_slot = descptr / sizeof(struct bcm43xx_dmadesc32);
}
assert(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);
#ifdef CONFIG_BCM43XX_DEBUG
if (++used_slots > ring->max_used_slots)
ring->max_used_slots = used_slots;
#endif
}
if (ring->dma64) {
bcm43xx_dma_write(ring, BCM43xx_DMA64_RXINDEX,
(u32)(slot * sizeof(struct bcm43xx_dmadesc64)));
} else {
bcm43xx_dma_write(ring, BCM43xx_DMA32_RXINDEX,
(u32)(slot * sizeof(struct bcm43xx_dmadesc32)));
}
ring->current_slot = slot;
}
void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring)
{
assert(ring->tx);
bcm43xx_power_saving_ctl_bits(ring->bcm, -1, 1);
if (ring->dma64) {
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL,
bcm43xx_dma_read(ring, BCM43xx_DMA64_TXCTL)
| BCM43xx_DMA64_TXSUSPEND);
} else {
bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL,
bcm43xx_dma_read(ring, BCM43xx_DMA32_TXCTL)
| BCM43xx_DMA32_TXSUSPEND);
}
}
void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring)
{
assert(ring->tx);
if (ring->dma64) {
bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL,
bcm43xx_dma_read(ring, BCM43xx_DMA64_TXCTL)
& ~BCM43xx_DMA64_TXSUSPEND);
} else {
bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL,
bcm43xx_dma_read(ring, BCM43xx_DMA32_TXCTL)
& ~BCM43xx_DMA32_TXSUSPEND);
}
bcm43xx_power_saving_ctl_bits(ring->bcm, -1, -1);
}
#ifndef BCM43xx_DMA_H_
#define BCM43xx_DMA_H_
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <linux/linkage.h>
#include <asm/atomic.h>
/* DMA-Interrupt reasons. */
#define BCM43xx_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \
| (1 << 14) | (1 << 15))
#define BCM43xx_DMAIRQ_NONFATALMASK (1 << 13)
#define BCM43xx_DMAIRQ_RX_DONE (1 << 16)
/*** 32-bit DMA Engine. ***/
/* 32-bit DMA controller registers. */
#define BCM43xx_DMA32_TXCTL 0x00
#define BCM43xx_DMA32_TXENABLE 0x00000001
#define BCM43xx_DMA32_TXSUSPEND 0x00000002
#define BCM43xx_DMA32_TXLOOPBACK 0x00000004
#define BCM43xx_DMA32_TXFLUSH 0x00000010
#define BCM43xx_DMA32_TXADDREXT_MASK 0x00030000
#define BCM43xx_DMA32_TXADDREXT_SHIFT 16
#define BCM43xx_DMA32_TXRING 0x04
#define BCM43xx_DMA32_TXINDEX 0x08
#define BCM43xx_DMA32_TXSTATUS 0x0C
#define BCM43xx_DMA32_TXDPTR 0x00000FFF
#define BCM43xx_DMA32_TXSTATE 0x0000F000
#define BCM43xx_DMA32_TXSTAT_DISABLED 0x00000000
#define BCM43xx_DMA32_TXSTAT_ACTIVE 0x00001000
#define BCM43xx_DMA32_TXSTAT_IDLEWAIT 0x00002000
#define BCM43xx_DMA32_TXSTAT_STOPPED 0x00003000
#define BCM43xx_DMA32_TXSTAT_SUSP 0x00004000
#define BCM43xx_DMA32_TXERROR 0x000F0000
#define BCM43xx_DMA32_TXERR_NOERR 0x00000000
#define BCM43xx_DMA32_TXERR_PROT 0x00010000
#define BCM43xx_DMA32_TXERR_UNDERRUN 0x00020000
#define BCM43xx_DMA32_TXERR_BUFREAD 0x00030000
#define BCM43xx_DMA32_TXERR_DESCREAD 0x00040000
#define BCM43xx_DMA32_TXACTIVE 0xFFF00000
#define BCM43xx_DMA32_RXCTL 0x10
#define BCM43xx_DMA32_RXENABLE 0x00000001
#define BCM43xx_DMA32_RXFROFF_MASK 0x000000FE
#define BCM43xx_DMA32_RXFROFF_SHIFT 1
#define BCM43xx_DMA32_RXDIRECTFIFO 0x00000100
#define BCM43xx_DMA32_RXADDREXT_MASK 0x00030000
#define BCM43xx_DMA32_RXADDREXT_SHIFT 16
#define BCM43xx_DMA32_RXRING 0x14
#define BCM43xx_DMA32_RXINDEX 0x18
#define BCM43xx_DMA32_RXSTATUS 0x1C
#define BCM43xx_DMA32_RXDPTR 0x00000FFF
#define BCM43xx_DMA32_RXSTATE 0x0000F000
#define BCM43xx_DMA32_RXSTAT_DISABLED 0x00000000
#define BCM43xx_DMA32_RXSTAT_ACTIVE 0x00001000
#define BCM43xx_DMA32_RXSTAT_IDLEWAIT 0x00002000
#define BCM43xx_DMA32_RXSTAT_STOPPED 0x00003000
#define BCM43xx_DMA32_RXERROR 0x000F0000
#define BCM43xx_DMA32_RXERR_NOERR 0x00000000
#define BCM43xx_DMA32_RXERR_PROT 0x00010000
#define BCM43xx_DMA32_RXERR_OVERFLOW 0x00020000
#define BCM43xx_DMA32_RXERR_BUFWRITE 0x00030000
#define BCM43xx_DMA32_RXERR_DESCREAD 0x00040000
#define BCM43xx_DMA32_RXACTIVE 0xFFF00000
/* 32-bit DMA descriptor. */
struct bcm43xx_dmadesc32 {
__le32 control;
__le32 address;
} __attribute__((__packed__));
#define BCM43xx_DMA32_DCTL_BYTECNT 0x00001FFF
#define BCM43xx_DMA32_DCTL_ADDREXT_MASK 0x00030000
#define BCM43xx_DMA32_DCTL_ADDREXT_SHIFT 16
#define BCM43xx_DMA32_DCTL_DTABLEEND 0x10000000
#define BCM43xx_DMA32_DCTL_IRQ 0x20000000
#define BCM43xx_DMA32_DCTL_FRAMEEND 0x40000000
#define BCM43xx_DMA32_DCTL_FRAMESTART 0x80000000
/* Address field Routing value. */
#define BCM43xx_DMA32_ROUTING 0xC0000000
#define BCM43xx_DMA32_ROUTING_SHIFT 30
#define BCM43xx_DMA32_NOTRANS 0x00000000
#define BCM43xx_DMA32_CLIENTTRANS 0x40000000
/*** 64-bit DMA Engine. ***/
/* 64-bit DMA controller registers. */
#define BCM43xx_DMA64_TXCTL 0x00
#define BCM43xx_DMA64_TXENABLE 0x00000001
#define BCM43xx_DMA64_TXSUSPEND 0x00000002
#define BCM43xx_DMA64_TXLOOPBACK 0x00000004
#define BCM43xx_DMA64_TXFLUSH 0x00000010
#define BCM43xx_DMA64_TXADDREXT_MASK 0x00030000
#define BCM43xx_DMA64_TXADDREXT_SHIFT 16
#define BCM43xx_DMA64_TXINDEX 0x04
#define BCM43xx_DMA64_TXRINGLO 0x08
#define BCM43xx_DMA64_TXRINGHI 0x0C
#define BCM43xx_DMA64_TXSTATUS 0x10
#define BCM43xx_DMA64_TXSTATDPTR 0x00001FFF
#define BCM43xx_DMA64_TXSTAT 0xF0000000
#define BCM43xx_DMA64_TXSTAT_DISABLED 0x00000000
#define BCM43xx_DMA64_TXSTAT_ACTIVE 0x10000000
#define BCM43xx_DMA64_TXSTAT_IDLEWAIT 0x20000000
#define BCM43xx_DMA64_TXSTAT_STOPPED 0x30000000
#define BCM43xx_DMA64_TXSTAT_SUSP 0x40000000
#define BCM43xx_DMA64_TXERROR 0x14
#define BCM43xx_DMA64_TXERRDPTR 0x0001FFFF
#define BCM43xx_DMA64_TXERR 0xF0000000
#define BCM43xx_DMA64_TXERR_NOERR 0x00000000
#define BCM43xx_DMA64_TXERR_PROT 0x10000000
#define BCM43xx_DMA64_TXERR_UNDERRUN 0x20000000
#define BCM43xx_DMA64_TXERR_TRANSFER 0x30000000
#define BCM43xx_DMA64_TXERR_DESCREAD 0x40000000
#define BCM43xx_DMA64_TXERR_CORE 0x50000000
#define BCM43xx_DMA64_RXCTL 0x20
#define BCM43xx_DMA64_RXENABLE 0x00000001
#define BCM43xx_DMA64_RXFROFF_MASK 0x000000FE
#define BCM43xx_DMA64_RXFROFF_SHIFT 1
#define BCM43xx_DMA64_RXDIRECTFIFO 0x00000100
#define BCM43xx_DMA64_RXADDREXT_MASK 0x00030000
#define BCM43xx_DMA64_RXADDREXT_SHIFT 16
#define BCM43xx_DMA64_RXINDEX 0x24
#define BCM43xx_DMA64_RXRINGLO 0x28
#define BCM43xx_DMA64_RXRINGHI 0x2C
#define BCM43xx_DMA64_RXSTATUS 0x30
#define BCM43xx_DMA64_RXSTATDPTR 0x00001FFF
#define BCM43xx_DMA64_RXSTAT 0xF0000000
#define BCM43xx_DMA64_RXSTAT_DISABLED 0x00000000
#define BCM43xx_DMA64_RXSTAT_ACTIVE 0x10000000
#define BCM43xx_DMA64_RXSTAT_IDLEWAIT 0x20000000
#define BCM43xx_DMA64_RXSTAT_STOPPED 0x30000000
#define BCM43xx_DMA64_RXSTAT_SUSP 0x40000000
#define BCM43xx_DMA64_RXERROR 0x34
#define BCM43xx_DMA64_RXERRDPTR 0x0001FFFF
#define BCM43xx_DMA64_RXERR 0xF0000000
#define BCM43xx_DMA64_RXERR_NOERR 0x00000000
#define BCM43xx_DMA64_RXERR_PROT 0x10000000
#define BCM43xx_DMA64_RXERR_UNDERRUN 0x20000000
#define BCM43xx_DMA64_RXERR_TRANSFER 0x30000000
#define BCM43xx_DMA64_RXERR_DESCREAD 0x40000000
#define BCM43xx_DMA64_RXERR_CORE 0x50000000
/* 64-bit DMA descriptor. */
struct bcm43xx_dmadesc64 {
__le32 control0;
__le32 control1;
__le32 address_low;
__le32 address_high;
} __attribute__((__packed__));
#define BCM43xx_DMA64_DCTL0_DTABLEEND 0x10000000
#define BCM43xx_DMA64_DCTL0_IRQ 0x20000000
#define BCM43xx_DMA64_DCTL0_FRAMEEND 0x40000000
#define BCM43xx_DMA64_DCTL0_FRAMESTART 0x80000000
#define BCM43xx_DMA64_DCTL1_BYTECNT 0x00001FFF
#define BCM43xx_DMA64_DCTL1_ADDREXT_MASK 0x00030000
#define BCM43xx_DMA64_DCTL1_ADDREXT_SHIFT 16
/* Address field Routing value. */
#define BCM43xx_DMA64_ROUTING 0xC0000000
#define BCM43xx_DMA64_ROUTING_SHIFT 30
#define BCM43xx_DMA64_NOTRANS 0x00000000
#define BCM43xx_DMA64_CLIENTTRANS 0x80000000
struct bcm43xx_dmadesc_generic {
union {
struct bcm43xx_dmadesc32 dma32;
struct bcm43xx_dmadesc64 dma64;
} __attribute__((__packed__));
} __attribute__((__packed__));
/* Misc DMA constants */
#define BCM43xx_DMA_RINGMEMSIZE PAGE_SIZE
#define BCM43xx_DMA0_RX_FRAMEOFFSET 30
#define BCM43xx_DMA3_RX_FRAMEOFFSET 0
/* DMA engine tuning knobs */
#define BCM43xx_TXRING_SLOTS 512
#define BCM43xx_RXRING_SLOTS 64
#define BCM43xx_DMA0_RX_BUFFERSIZE (2304 + 100)
#define BCM43xx_DMA3_RX_BUFFERSIZE 16
/* Suspend the tx queue, if less than this percent slots are free. */
#define BCM43xx_TXSUSPEND_PERCENT 20
/* Resume the tx queue, if more than this percent slots are free. */
#define BCM43xx_TXRESUME_PERCENT 50
#ifdef CONFIG_BCM43XX_DMA
struct sk_buff;
struct bcm43xx_private;
struct bcm43xx_xmitstatus;
struct bcm43xx_dmadesc_meta {
/* The kernel DMA-able buffer. */
struct sk_buff *skb;
/* DMA base bus-address of the descriptor buffer. */
dma_addr_t dmaaddr;
};
struct bcm43xx_dmaring {
/* Kernel virtual base address of the ring memory. */
void *descbase;
/* Meta data about all descriptors. */
struct bcm43xx_dmadesc_meta *meta;
/* DMA Routing value. */
u32 routing;
/* (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;
/* Marks to suspend/resume the queue. */
int suspend_mark;
int resume_mark;
/* 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? */
u8 tx;
/* Boolean. 64bit DMA if true, 32bit DMA otherwise. */
u8 dma64;
/* Boolean. Are transfers suspended on this ring? */
u8 suspended;
struct bcm43xx_private *bcm;
#ifdef CONFIG_BCM43XX_DEBUG
/* Maximum number of used slots. */
int max_used_slots;
#endif /* CONFIG_BCM43XX_DEBUG*/
};
static inline
int bcm43xx_dma_desc2idx(struct bcm43xx_dmaring *ring,
struct bcm43xx_dmadesc_generic *desc)
{
if (ring->dma64) {
struct bcm43xx_dmadesc64 *dd64 = ring->descbase;
return (int)(&(desc->dma64) - dd64);
} else {
struct bcm43xx_dmadesc32 *dd32 = ring->descbase;
return (int)(&(desc->dma32) - dd32);
}
}
static inline
struct bcm43xx_dmadesc_generic * bcm43xx_dma_idx2desc(struct bcm43xx_dmaring *ring,
int slot,
struct bcm43xx_dmadesc_meta **meta)
{
*meta = &(ring->meta[slot]);
if (ring->dma64) {
struct bcm43xx_dmadesc64 *dd64 = ring->descbase;
return (struct bcm43xx_dmadesc_generic *)(&(dd64[slot]));
} else {
struct bcm43xx_dmadesc32 *dd32 = ring->descbase;
return (struct bcm43xx_dmadesc_generic *)(&(dd32[slot]));
}
}
static inline
u32 bcm43xx_dma_read(struct bcm43xx_dmaring *ring,
u16 offset)
{
return bcm43xx_read32(ring->bcm, ring->mmio_base + offset);
}
static inline
void bcm43xx_dma_write(struct bcm43xx_dmaring *ring,
u16 offset, u32 value)
{
bcm43xx_write32(ring->bcm, ring->mmio_base + offset, value);
}
int bcm43xx_dma_init(struct bcm43xx_private *bcm);
void bcm43xx_dma_free(struct bcm43xx_private *bcm);
int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,
u16 dmacontroller_mmio_base,
int dma64);
int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,
u16 dmacontroller_mmio_base,
int dma64);
u16 bcm43xx_dmacontroller_base(int dma64bit, int dmacontroller_idx);
void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring);
void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring);
void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status);
int bcm43xx_dma_tx(struct bcm43xx_private *bcm,
struct ieee80211_txb *txb);
void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring);
/* Helper function that returns the dma mask for this device. */
static inline
u64 bcm43xx_get_supported_dma_mask(struct bcm43xx_private *bcm)
{
int dma64 = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH) &
BCM43xx_SBTMSTATEHIGH_DMA64BIT;
u16 mmio_base = bcm43xx_dmacontroller_base(dma64, 0);
u32 mask = BCM43xx_DMA32_TXADDREXT_MASK;
if (dma64)
return DMA_64BIT_MASK;
bcm43xx_write32(bcm, mmio_base + BCM43xx_DMA32_TXCTL, mask);
if (bcm43xx_read32(bcm, mmio_base + BCM43xx_DMA32_TXCTL) & mask)
return DMA_32BIT_MASK;
return DMA_30BIT_MASK;
}
#else /* CONFIG_BCM43XX_DMA */
static inline
int bcm43xx_dma_init(struct bcm43xx_private *bcm)
{
return 0;
}
static inline
void bcm43xx_dma_free(struct bcm43xx_private *bcm)
{
}
static inline
int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,
u16 dmacontroller_mmio_base,
int dma64)
{
return 0;
}
static inline
int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,
u16 dmacontroller_mmio_base,
int dma64)
{
return 0;
}
static inline
int bcm43xx_dma_tx(struct bcm43xx_private *bcm,
struct ieee80211_txb *txb)
{
return 0;
}
static inline
void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status)
{
}
static inline
void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)
{
}
static inline
void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring)
{
}
static inline
void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring)
{
}
#endif /* CONFIG_BCM43XX_DMA */
#endif /* BCM43xx_DMA_H_ */
/*
Broadcom BCM43xx wireless driver
ethtool support
Copyright (c) 2006 Jason Lunz <lunz@falooley.org>
Some code in this file is derived from the 8139too.c driver
Copyright (C) 2002 Jeff Garzik
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 "bcm43xx.h"
#include "bcm43xx_ethtool.h"
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/utsname.h>
static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
struct bcm43xx_private *bcm = bcm43xx_priv(dev);
strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strncpy(info->version, utsname()->release, sizeof(info->version));
strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN);
}
const struct ethtool_ops bcm43xx_ethtool_ops = {
.get_drvinfo = bcm43xx_get_drvinfo,
.get_link = ethtool_op_get_link,
};
#ifndef BCM43xx_ETHTOOL_H_
#define BCM43xx_ETHTOOL_H_
#include <linux/ethtool.h>
extern const struct ethtool_ops bcm43xx_ethtool_ops;
#endif /* BCM43xx_ETHTOOL_H_ */
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
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 "bcm43xx.h"
#include "bcm43xx_ilt.h"
#include "bcm43xx_phy.h"
/**** Initial Internal Lookup Tables ****/
const u32 bcm43xx_ilt_rotor[BCM43xx_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 bcm43xx_ilt_retard[BCM43xx_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 bcm43xx_ilt_finefreqa[BCM43xx_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 bcm43xx_ilt_finefreqg[BCM43xx_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 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE] = {
0x0001, 0x0001, 0x0001, 0xFFFE,
0xFFFE, 0x3FFF, 0x1000, 0x0393,
};
const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE] = {
0x4C4C, 0x4C4C, 0x4C4C, 0x2D36,
0x4C4C, 0x4C4C, 0x4C4C, 0x2D36,
};
const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE] = {
0x013C, 0x01F5, 0x031A, 0x0631,
0x0001, 0x0001, 0x0001, 0x0001,
};
const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE] = {
0x5484, 0x3C40, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
};
const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_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 bcm43xx_ilt_noisescaleg2[BCM43xx_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 bcm43xx_ilt_noisescaleg3[BCM43xx_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 bcm43xx_ilt_sigmasqr1[BCM43xx_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 bcm43xx_ilt_sigmasqr2[BCM43xx_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 bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val)
{
if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset);
mmiowb();
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, val);
} else {
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset);
mmiowb();
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA1, val);
}
}
void bcm43xx_ilt_write32(struct bcm43xx_private *bcm, u16 offset, u32 val)
{
if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset);
mmiowb();
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA2, (val & 0xFFFF0000) >> 16);
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, val & 0x0000FFFF);
} else {
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset);
mmiowb();
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA2, (val & 0xFFFF0000) >> 16);
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA1, val & 0x0000FFFF);
}
}
u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset)
{
if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset);
return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_A_DATA1);
} else {
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset);
return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_G_DATA1);
}
}
#ifndef BCM43xx_ILT_H_
#define BCM43xx_ILT_H_
#define BCM43xx_ILT_ROTOR_SIZE 53
extern const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE];
#define BCM43xx_ILT_RETARD_SIZE 53
extern const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE];
#define BCM43xx_ILT_FINEFREQA_SIZE 256
extern const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE];
#define BCM43xx_ILT_FINEFREQG_SIZE 256
extern const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE];
#define BCM43xx_ILT_NOISEA2_SIZE 8
extern const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE];
#define BCM43xx_ILT_NOISEA3_SIZE 8
extern const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE];
#define BCM43xx_ILT_NOISEG1_SIZE 8
extern const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE];
#define BCM43xx_ILT_NOISEG2_SIZE 8
extern const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE];
#define BCM43xx_ILT_NOISESCALEG_SIZE 27
extern const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE];
extern const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE];
extern const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE];
#define BCM43xx_ILT_SIGMASQR_SIZE 53
extern const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE];
extern const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE];
void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val);
void bcm43xx_ilt_write32(struct bcm43xx_private *bcm, u16 offset, u32 val);
u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset);
#endif /* BCM43xx_ILT_H_ */
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
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 "bcm43xx_leds.h"
#include "bcm43xx_radio.h"
#include "bcm43xx.h"
#include <linux/bitops.h>
static void bcm43xx_led_changestate(struct bcm43xx_led *led)
{
struct bcm43xx_private *bcm = led->bcm;
const int index = bcm43xx_led_index(led);
const u16 mask = (1 << index);
u16 ledctl;
assert(index >= 0 && index < BCM43xx_NR_LEDS);
assert(led->blink_interval);
ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);
bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
}
static void bcm43xx_led_blink(unsigned long d)
{
struct bcm43xx_led *led = (struct bcm43xx_led *)d;
struct bcm43xx_private *bcm = led->bcm;
unsigned long flags;
spin_lock_irqsave(&bcm->leds_lock, flags);
if (led->blink_interval) {
bcm43xx_led_changestate(led);
mod_timer(&led->blink_timer, jiffies + led->blink_interval);
}
spin_unlock_irqrestore(&bcm->leds_lock, flags);
}
static void bcm43xx_led_blink_start(struct bcm43xx_led *led,
unsigned long interval)
{
if (led->blink_interval)
return;
led->blink_interval = interval;
bcm43xx_led_changestate(led);
led->blink_timer.expires = jiffies + interval;
add_timer(&led->blink_timer);
}
static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync)
{
struct bcm43xx_private *bcm = led->bcm;
const int index = bcm43xx_led_index(led);
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. */
assert(index >= 0 && index < BCM43xx_NR_LEDS);
ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
if (led->activelow)
ledctl |= (1 << index);
else
ledctl &= ~(1 << index);
bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
}
static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm,
struct bcm43xx_led *led,
int led_index)
{
/* 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 BCM43xx_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 = BCM43xx_LED_ACTIVITY;
led->activelow = 1;
if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ)
led->behaviour = BCM43xx_LED_RADIO_ALL;
break;
case 1:
led->behaviour = BCM43xx_LED_RADIO_B;
if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK)
led->behaviour = BCM43xx_LED_ASSOC;
break;
case 2:
led->behaviour = BCM43xx_LED_RADIO_A;
break;
case 3:
led->behaviour = BCM43xx_LED_OFF;
break;
default:
assert(0);
}
}
int bcm43xx_leds_init(struct bcm43xx_private *bcm)
{
struct bcm43xx_led *led;
u8 sprom[4];
int i;
sprom[0] = bcm->sprom.wl0gpio0;
sprom[1] = bcm->sprom.wl0gpio1;
sprom[2] = bcm->sprom.wl0gpio2;
sprom[3] = bcm->sprom.wl0gpio3;
for (i = 0; i < BCM43xx_NR_LEDS; i++) {
led = &(bcm->leds[i]);
led->bcm = bcm;
setup_timer(&led->blink_timer,
bcm43xx_led_blink,
(unsigned long)led);
if (sprom[i] == 0xFF) {
bcm43xx_led_init_hardcoded(bcm, led, i);
} else {
led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR;
led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW);
}
}
return 0;
}
void bcm43xx_leds_exit(struct bcm43xx_private *bcm)
{
struct bcm43xx_led *led;
int i;
for (i = 0; i < BCM43xx_NR_LEDS; i++) {
led = &(bcm->leds[i]);
bcm43xx_led_blink_stop(led, 1);
}
bcm43xx_leds_switch_all(bcm, 0);
}
void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity)
{
struct bcm43xx_led *led;
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES;
int i, turn_on;
unsigned long interval = 0;
u16 ledctl;
unsigned long flags;
spin_lock_irqsave(&bcm->leds_lock, flags);
ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
for (i = 0; i < BCM43xx_NR_LEDS; i++) {
led = &(bcm->leds[i]);
turn_on = 0;
switch (led->behaviour) {
case BCM43xx_LED_INACTIVE:
continue;
case BCM43xx_LED_OFF:
case BCM43xx_LED_BCM4303_3:
break;
case BCM43xx_LED_ON:
turn_on = 1;
break;
case BCM43xx_LED_ACTIVITY:
case BCM43xx_LED_BCM4303_0:
turn_on = activity;
break;
case BCM43xx_LED_RADIO_ALL:
turn_on = radio->enabled && bcm43xx_is_hw_radio_enabled(bcm);
break;
case BCM43xx_LED_RADIO_A:
case BCM43xx_LED_BCM4303_2:
turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) &&
phy->type == BCM43xx_PHYTYPE_A);
break;
case BCM43xx_LED_RADIO_B:
case BCM43xx_LED_BCM4303_1:
turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) &&
(phy->type == BCM43xx_PHYTYPE_B ||
phy->type == BCM43xx_PHYTYPE_G));
break;
case BCM43xx_LED_MODE_BG:
if (phy->type == BCM43xx_PHYTYPE_G && bcm43xx_is_hw_radio_enabled(bcm) &&
1/*FIXME: using G rates.*/)
turn_on = 1;
break;
case BCM43xx_LED_TRANSFER:
if (transferring)
bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);
else
bcm43xx_led_blink_stop(led, 0);
continue;
case BCM43xx_LED_APTRANSFER:
if (bcm->ieee->iw_mode == IW_MODE_MASTER) {
if (transferring) {
interval = BCM43xx_LEDBLINK_FAST;
turn_on = 1;
}
} else {
turn_on = 1;
if (0/*TODO: not assoc*/)
interval = BCM43xx_LEDBLINK_SLOW;
else if (transferring)
interval = BCM43xx_LEDBLINK_FAST;
else
turn_on = 0;
}
if (turn_on)
bcm43xx_led_blink_start(led, interval);
else
bcm43xx_led_blink_stop(led, 0);
continue;
case BCM43xx_LED_WEIRD:
//TODO
break;
case BCM43xx_LED_ASSOC:
if (bcm->softmac->associnfo.associated)
turn_on = 1;
break;
#ifdef CONFIG_BCM43XX_DEBUG
case BCM43xx_LED_TEST_BLINKSLOW:
bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW);
continue;
case BCM43xx_LED_TEST_BLINKMEDIUM:
bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);
continue;
case BCM43xx_LED_TEST_BLINKFAST:
bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST);
continue;
#endif /* CONFIG_BCM43XX_DEBUG */
default:
dprintkl(KERN_INFO PFX "Bad value in leds_update,"
" led->behaviour: 0x%x\n", led->behaviour);
};
if (led->activelow)
turn_on = !turn_on;
if (turn_on)
ledctl |= (1 << i);
else
ledctl &= ~(1 << i);
}
bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
spin_unlock_irqrestore(&bcm->leds_lock, flags);
}
void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on)
{
struct bcm43xx_led *led;
u16 ledctl;
int i;
int bit_on;
unsigned long flags;
spin_lock_irqsave(&bcm->leds_lock, flags);
ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
for (i = 0; i < BCM43xx_NR_LEDS; i++) {
led = &(bcm->leds[i]);
if (led->behaviour == BCM43xx_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);
}
bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
spin_unlock_irqrestore(&bcm->leds_lock, flags);
}
#ifndef BCM43xx_LEDS_H_
#define BCM43xx_LEDS_H_
#include <linux/types.h>
#include <linux/timer.h>
struct bcm43xx_led {
u8 behaviour:7;
u8 activelow:1;
struct bcm43xx_private *bcm;
struct timer_list blink_timer;
unsigned long blink_interval;
};
#define bcm43xx_led_index(led) ((int)((led) - (led)->bcm->leds))
/* Delay between state changes when blinking in jiffies */
#define BCM43xx_LEDBLINK_SLOW (HZ / 1)
#define BCM43xx_LEDBLINK_MEDIUM (HZ / 4)
#define BCM43xx_LEDBLINK_FAST (HZ / 8)
#define BCM43xx_LED_XFER_THRES (HZ / 100)
#define BCM43xx_LED_BEHAVIOUR 0x7F
#define BCM43xx_LED_ACTIVELOW 0x80
enum { /* LED behaviour values */
BCM43xx_LED_OFF,
BCM43xx_LED_ON,
BCM43xx_LED_ACTIVITY,
BCM43xx_LED_RADIO_ALL,
BCM43xx_LED_RADIO_A,
BCM43xx_LED_RADIO_B,
BCM43xx_LED_MODE_BG,
BCM43xx_LED_TRANSFER,
BCM43xx_LED_APTRANSFER,
BCM43xx_LED_WEIRD,//FIXME
BCM43xx_LED_ASSOC,
BCM43xx_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.
*/
BCM43xx_LED_TEST_BLINKSLOW,
BCM43xx_LED_TEST_BLINKMEDIUM,
BCM43xx_LED_TEST_BLINKFAST,
/* Misc values for BCM4303 */
BCM43xx_LED_BCM4303_0 = 0x2B,
BCM43xx_LED_BCM4303_1 = 0x78,
BCM43xx_LED_BCM4303_2 = 0x2E,
BCM43xx_LED_BCM4303_3 = 0x19,
};
int bcm43xx_leds_init(struct bcm43xx_private *bcm);
void bcm43xx_leds_exit(struct bcm43xx_private *bcm);
void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity);
void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on);
#endif /* BCM43xx_LEDS_H_ */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef BCM43xx_MAIN_H_
#define BCM43xx_MAIN_H_
#include "bcm43xx.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 bcm43xx_freq_to_channel_a(int freq)
{
return ((freq - 5000) / 5);
}
static inline
u8 bcm43xx_freq_to_channel_bg(int freq)
{
u8 channel;
if (freq == 2484)
channel = 14;
else
channel = (freq - 2407) / 5;
return channel;
}
static inline
u8 bcm43xx_freq_to_channel(struct bcm43xx_private *bcm,
int freq)
{
if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
return bcm43xx_freq_to_channel_a(freq);
return bcm43xx_freq_to_channel_bg(freq);
}
/* Lightweight function to convert a channel number to a frequency (in Mhz). */
static inline
int bcm43xx_channel_to_freq_a(u8 channel)
{
return (5000 + (5 * channel));
}
static inline
int bcm43xx_channel_to_freq_bg(u8 channel)
{
int freq;
if (channel == 14)
freq = 2484;
else
freq = 2407 + (5 * channel);
return freq;
}
static inline
int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm,
u8 channel)
{
if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
return bcm43xx_channel_to_freq_a(channel);
return bcm43xx_channel_to_freq_bg(channel);
}
void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf);
void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf);
void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,
int iw_mode);
u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm,
u16 routing, u16 offset);
u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm,
u16 routing, u16 offset);
void bcm43xx_shm_write32(struct bcm43xx_private *bcm,
u16 routing, u16 offset,
u32 value);
void bcm43xx_shm_write16(struct bcm43xx_private *bcm,
u16 routing, u16 offset,
u16 value);
void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm);
int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core);
int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
int phytype);
void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);
void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
void bcm43xx_mac_enable(struct bcm43xx_private *bcm);
void bcm43xx_cancel_work(struct bcm43xx_private *bcm);
void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm);
void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason);
int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom);
int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom);
#endif /* BCM43xx_MAIN_H_ */
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/types.h>
#include "bcm43xx.h"
#include "bcm43xx_phy.h"
#include "bcm43xx_main.h"
#include "bcm43xx_radio.h"
#include "bcm43xx_ilt.h"
#include "bcm43xx_power.h"
static const s8 bcm43xx_tssi2dbm_b_table[] = {
0x4D, 0x4C, 0x4B, 0x4A,
0x4A, 0x49, 0x48, 0x47,
0x47, 0x46, 0x45, 0x45,
0x44, 0x43, 0x42, 0x42,
0x41, 0x40, 0x3F, 0x3E,
0x3D, 0x3C, 0x3B, 0x3A,
0x39, 0x38, 0x37, 0x36,
0x35, 0x34, 0x32, 0x31,
0x30, 0x2F, 0x2D, 0x2C,
0x2B, 0x29, 0x28, 0x26,
0x25, 0x23, 0x21, 0x1F,
0x1D, 0x1A, 0x17, 0x14,
0x10, 0x0C, 0x06, 0x00,
-7, -7, -7, -7,
-7, -7, -7, -7,
-7, -7, -7, -7,
};
static const s8 bcm43xx_tssi2dbm_g_table[] = {
77, 77, 77, 76,
76, 76, 75, 75,
74, 74, 73, 73,
73, 72, 72, 71,
71, 70, 70, 69,
68, 68, 67, 67,
66, 65, 65, 64,
63, 63, 62, 61,
60, 59, 58, 57,
56, 55, 54, 53,
52, 50, 49, 47,
45, 43, 40, 37,
33, 28, 22, 14,
5, -7, -20, -20,
-20, -20, -20, -20,
-20, -20, -20, -20,
};
static void bcm43xx_phy_initg(struct bcm43xx_private *bcm);
static inline
void bcm43xx_voluntary_preempt(void)
{
assert(!in_atomic() && !in_irq() &&
!in_interrupt() && !irqs_disabled());
#ifndef CONFIG_PREEMPT
cond_resched();
#endif /* CONFIG_PREEMPT */
}
void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
assert(irqs_disabled());
if (bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) == 0x00000000) {
phy->is_locked = 0;
return;
}
if (bcm->current_core->rev < 3) {
bcm43xx_mac_suspend(bcm);
spin_lock(&phy->lock);
} else {
if (bcm->ieee->iw_mode != IW_MODE_MASTER)
bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
}
phy->is_locked = 1;
}
void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
assert(irqs_disabled());
if (bcm->current_core->rev < 3) {
if (phy->is_locked) {
spin_unlock(&phy->lock);
bcm43xx_mac_enable(bcm);
}
} else {
if (bcm->ieee->iw_mode != IW_MODE_MASTER)
bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
}
phy->is_locked = 0;
}
u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset)
{
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset);
return bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_DATA);
}
void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val)
{
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset);
mmiowb();
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_DATA, val);
}
void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */
if (phy->calibrated)
return;
if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) {
bcm43xx_wireless_core_reset(bcm, 0);
bcm43xx_phy_initg(bcm);
bcm43xx_wireless_core_reset(bcm, 1);
}
phy->calibrated = 1;
}
/* Connect the PHY
* http://bcm-specs.sipsolutions.net/SetPHY
*/
int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u32 flags;
if (bcm->current_core->rev < 5)
goto out;
flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
if (connect) {
if (!(flags & BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL))
return -ENODEV;
flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE;
bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
} else {
if (!(flags & BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL))
return -ENODEV;
flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
flags &= ~BCM43xx_SBTMSTATELOW_G_MODE_ENABLE;
bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
}
out:
phy->connected = connect;
if (connect)
dprintk(KERN_INFO PFX "PHY connected\n");
else
dprintk(KERN_INFO PFX "PHY disconnected\n");
return 0;
}
/* intialize B PHY power control
* as described in http://bcm-specs.sipsolutions.net/InitPowerControl
*/
static void bcm43xx_phy_init_pctl(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 saved_batt = 0, saved_ratt = 0, saved_txctl1 = 0;
int must_reset_txpower = 0;
assert(phy->type != BCM43xx_PHYTYPE_A);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type == 0x0416))
return;
bcm43xx_phy_write(bcm, 0x0028, 0x8018);
bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF);
if (phy->type == BCM43xx_PHYTYPE_G) {
if (!phy->connected)
return;
bcm43xx_phy_write(bcm, 0x047A, 0xC111);
}
if (phy->savedpctlreg != 0xFFFF)
return;
if (phy->type == BCM43xx_PHYTYPE_B &&
phy->rev >= 2 &&
radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0076,
bcm43xx_radio_read16(bcm, 0x0076) | 0x0084);
} else {
saved_batt = radio->baseband_atten;
saved_ratt = radio->radio_atten;
saved_txctl1 = radio->txctl1;
if ((radio->revision >= 6) && (radio->revision <= 8)
&& /*FIXME: incomplete specs for 5 < revision < 9 */ 0)
bcm43xx_radio_set_txpower_bg(bcm, 0xB, 0x1F, 0);
else
bcm43xx_radio_set_txpower_bg(bcm, 0xB, 9, 0);
must_reset_txpower = 1;
}
bcm43xx_dummy_transmission(bcm);
phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_PCTL);
if (must_reset_txpower)
bcm43xx_radio_set_txpower_bg(bcm, saved_batt, saved_ratt, saved_txctl1);
else
bcm43xx_radio_write16(bcm, 0x0076, bcm43xx_radio_read16(bcm, 0x0076) & 0xFF7B);
bcm43xx_radio_clear_tssi(bcm);
}
static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 offset = 0x0000;
if (phy->rev == 1)
offset = 0x4C00;
bcm43xx_ilt_write(bcm, offset, 0x00FE);
bcm43xx_ilt_write(bcm, offset + 1, 0x000D);
bcm43xx_ilt_write(bcm, offset + 2, 0x0013);
bcm43xx_ilt_write(bcm, offset + 3, 0x0019);
if (phy->rev == 1) {
bcm43xx_ilt_write(bcm, 0x1800, 0x2710);
bcm43xx_ilt_write(bcm, 0x1801, 0x9B83);
bcm43xx_ilt_write(bcm, 0x1802, 0x9B83);
bcm43xx_ilt_write(bcm, 0x1803, 0x0F8D);
bcm43xx_phy_write(bcm, 0x0455, 0x0004);
}
bcm43xx_phy_write(bcm, 0x04A5, (bcm43xx_phy_read(bcm, 0x04A5) & 0x00FF) | 0x5700);
bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xFF80) | 0x000F);
bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xC07F) | 0x2B80);
bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xF0FF) | 0x0300);
bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0008);
bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xFFF0) | 0x0008);
bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xF0FF) | 0x0600);
bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xF0FF) | 0x0700);
bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xF0FF) | 0x0100);
if (phy->rev == 1)
bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xFFF0) | 0x0007);
bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xFF00) | 0x001C);
bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xC0FF) | 0x0200);
bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0xFF00) | 0x001C);
bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xFF00) | 0x0020);
bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xC0FF) | 0x0200);
bcm43xx_phy_write(bcm, 0x0482, (bcm43xx_phy_read(bcm, 0x0482) & 0xFF00) | 0x002E);
bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0x00FF) | 0x1A00);
bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0xFF00) | 0x0028);
bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0x00FF) | 0x2C00);
if (phy->rev == 1) {
bcm43xx_phy_write(bcm, 0x0430, 0x092B);
bcm43xx_phy_write(bcm, 0x041B, (bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1) | 0x0002);
} else {
bcm43xx_phy_write(bcm, 0x041B, bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1);
bcm43xx_phy_write(bcm, 0x041F, 0x287A);
bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0xFFF0) | 0x0004);
}
if (phy->rev > 2) {
bcm43xx_phy_write(bcm, 0x0422, 0x287A);
bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420)
& 0x0FFF) | 0x3000);
}
bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080)
| 0x7874);
bcm43xx_phy_write(bcm, 0x048E, 0x1C00);
if (phy->rev == 1) {
bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB)
& 0xF0FF) | 0x0600);
bcm43xx_phy_write(bcm, 0x048B, 0x005E);
bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C)
& 0xFF00) | 0x001E);
bcm43xx_phy_write(bcm, 0x048D, 0x0002);
}
bcm43xx_ilt_write(bcm, offset + 0x0800, 0);
bcm43xx_ilt_write(bcm, offset + 0x0801, 7);
bcm43xx_ilt_write(bcm, offset + 0x0802, 16);
bcm43xx_ilt_write(bcm, offset + 0x0803, 28);
if (phy->rev >= 6) {
bcm43xx_phy_write(bcm, 0x0426, (bcm43xx_phy_read(bcm, 0x0426)
& 0xFFFC));
bcm43xx_phy_write(bcm, 0x0426, (bcm43xx_phy_read(bcm, 0x0426)
& 0xEFFF));
}
}
static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 i;
assert(phy->type == BCM43xx_PHYTYPE_G);
if (phy->rev == 1) {
bcm43xx_phy_write(bcm, 0x0406, 0x4F19);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
(bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS)
& 0xFC3F) | 0x0340);
bcm43xx_phy_write(bcm, 0x042C, 0x005A);
bcm43xx_phy_write(bcm, 0x0427, 0x001A);
for (i = 0; i < BCM43xx_ILT_FINEFREQG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqg[i]);
for (i = 0; i < BCM43xx_ILT_NOISEG1_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg1[i]);
for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++)
bcm43xx_ilt_write32(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]);
} else {
/* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */
bcm43xx_nrssi_hw_write(bcm, 0xBA98, (s16)0x7654);
if (phy->rev == 2) {
bcm43xx_phy_write(bcm, 0x04C0, 0x1861);
bcm43xx_phy_write(bcm, 0x04C1, 0x0271);
} else if (phy->rev > 2) {
bcm43xx_phy_write(bcm, 0x04C0, 0x0098);
bcm43xx_phy_write(bcm, 0x04C1, 0x0070);
bcm43xx_phy_write(bcm, 0x04C9, 0x0080);
}
bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x800);
for (i = 0; i < 64; i++)
bcm43xx_ilt_write(bcm, 0x4000 + i, i);
for (i = 0; i < BCM43xx_ILT_NOISEG2_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg2[i]);
}
if (phy->rev <= 2)
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]);
else if ((phy->rev >= 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]);
else
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg2[i]);
if (phy->rev == 2)
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
else if ((phy->rev > 2) && (phy->rev <= 8))
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]);
if (phy->rev == 1) {
for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++)
bcm43xx_ilt_write32(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]);
for (i = 0; i < 4; i++) {
bcm43xx_ilt_write(bcm, 0x5404 + i, 0x0020);
bcm43xx_ilt_write(bcm, 0x5408 + i, 0x0020);
bcm43xx_ilt_write(bcm, 0x540C + i, 0x0020);
bcm43xx_ilt_write(bcm, 0x5410 + i, 0x0020);
}
bcm43xx_phy_agcsetup(bcm);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type == 0x0416) &&
(bcm->board_revision == 0x0017))
return;
bcm43xx_ilt_write(bcm, 0x5001, 0x0002);
bcm43xx_ilt_write(bcm, 0x5002, 0x0001);
} else {
for (i = 0; i <= 0x2F; i++)
bcm43xx_ilt_write(bcm, 0x1000 + i, 0x0820);
bcm43xx_phy_agcsetup(bcm);
bcm43xx_phy_read(bcm, 0x0400); /* dummy read */
bcm43xx_phy_write(bcm, 0x0403, 0x1000);
bcm43xx_ilt_write(bcm, 0x3C02, 0x000F);
bcm43xx_ilt_write(bcm, 0x3C03, 0x0014);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type == 0x0416) &&
(bcm->board_revision == 0x0017))
return;
bcm43xx_ilt_write(bcm, 0x0401, 0x0002);
bcm43xx_ilt_write(bcm, 0x0402, 0x0001);
}
}
/* Initialize the noisescaletable for APHY */
static void bcm43xx_phy_init_noisescaletbl(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int i;
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, 0x1400);
for (i = 0; i < 12; i++) {
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323);
}
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6700);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2300);
for (i = 0; i < 11; i++) {
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323);
}
if (phy->rev == 2)
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0067);
else
bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0023);
}
static void bcm43xx_phy_setupa(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 i;
assert(phy->type == BCM43xx_PHYTYPE_A);
switch (phy->rev) {
case 2:
bcm43xx_phy_write(bcm, 0x008E, 0x3800);
bcm43xx_phy_write(bcm, 0x0035, 0x03FF);
bcm43xx_phy_write(bcm, 0x0036, 0x0400);
bcm43xx_ilt_write(bcm, 0x3807, 0x0051);
bcm43xx_phy_write(bcm, 0x001C, 0x0FF9);
bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F);
bcm43xx_ilt_write(bcm, 0x3C0C, 0x07BF);
bcm43xx_radio_write16(bcm, 0x0002, 0x07BF);
bcm43xx_phy_write(bcm, 0x0024, 0x4680);
bcm43xx_phy_write(bcm, 0x0020, 0x0003);
bcm43xx_phy_write(bcm, 0x001D, 0x0F40);
bcm43xx_phy_write(bcm, 0x001F, 0x1C00);
bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400);
bcm43xx_phy_write(bcm, 0x002B, bcm43xx_phy_read(bcm, 0x002B) & 0xFBFF);
bcm43xx_phy_write(bcm, 0x008E, 0x58C1);
bcm43xx_ilt_write(bcm, 0x0803, 0x000F);
bcm43xx_ilt_write(bcm, 0x0804, 0x001F);
bcm43xx_ilt_write(bcm, 0x0805, 0x002A);
bcm43xx_ilt_write(bcm, 0x0805, 0x0030);
bcm43xx_ilt_write(bcm, 0x0807, 0x003A);
bcm43xx_ilt_write(bcm, 0x0000, 0x0013);
bcm43xx_ilt_write(bcm, 0x0001, 0x0013);
bcm43xx_ilt_write(bcm, 0x0002, 0x0013);
bcm43xx_ilt_write(bcm, 0x0003, 0x0013);
bcm43xx_ilt_write(bcm, 0x0004, 0x0015);
bcm43xx_ilt_write(bcm, 0x0005, 0x0015);
bcm43xx_ilt_write(bcm, 0x0006, 0x0019);
bcm43xx_ilt_write(bcm, 0x0404, 0x0003);
bcm43xx_ilt_write(bcm, 0x0405, 0x0003);
bcm43xx_ilt_write(bcm, 0x0406, 0x0007);
for (i = 0; i < 16; i++)
bcm43xx_ilt_write(bcm, 0x4000 + i, (0x8 + i) & 0x000F);
bcm43xx_ilt_write(bcm, 0x3003, 0x1044);
bcm43xx_ilt_write(bcm, 0x3004, 0x7201);
bcm43xx_ilt_write(bcm, 0x3006, 0x0040);
bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008);
for (i = 0; i < BCM43xx_ILT_FINEFREQA_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqa[i]);
for (i = 0; i < BCM43xx_ILT_NOISEA2_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea2[i]);
for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++)
bcm43xx_ilt_write32(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]);
bcm43xx_phy_init_noisescaletbl(bcm);
for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++)
bcm43xx_ilt_write32(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]);
break;
case 3:
for (i = 0; i < 64; i++)
bcm43xx_ilt_write(bcm, 0x4000 + i, i);
bcm43xx_ilt_write(bcm, 0x3807, 0x0051);
bcm43xx_phy_write(bcm, 0x001C, 0x0FF9);
bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F);
bcm43xx_radio_write16(bcm, 0x0002, 0x07BF);
bcm43xx_phy_write(bcm, 0x0024, 0x4680);
bcm43xx_phy_write(bcm, 0x0020, 0x0003);
bcm43xx_phy_write(bcm, 0x001D, 0x0F40);
bcm43xx_phy_write(bcm, 0x001F, 0x1C00);
bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400);
bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008);
for (i = 0; i < BCM43xx_ILT_NOISEA3_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea3[i]);
bcm43xx_phy_init_noisescaletbl(bcm);
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
bcm43xx_phy_write(bcm, 0x0003, 0x1808);
bcm43xx_ilt_write(bcm, 0x0803, 0x000F);
bcm43xx_ilt_write(bcm, 0x0804, 0x001F);
bcm43xx_ilt_write(bcm, 0x0805, 0x002A);
bcm43xx_ilt_write(bcm, 0x0805, 0x0030);
bcm43xx_ilt_write(bcm, 0x0807, 0x003A);
bcm43xx_ilt_write(bcm, 0x0000, 0x0013);
bcm43xx_ilt_write(bcm, 0x0001, 0x0013);
bcm43xx_ilt_write(bcm, 0x0002, 0x0013);
bcm43xx_ilt_write(bcm, 0x0003, 0x0013);
bcm43xx_ilt_write(bcm, 0x0004, 0x0015);
bcm43xx_ilt_write(bcm, 0x0005, 0x0015);
bcm43xx_ilt_write(bcm, 0x0006, 0x0019);
bcm43xx_ilt_write(bcm, 0x0404, 0x0003);
bcm43xx_ilt_write(bcm, 0x0405, 0x0003);
bcm43xx_ilt_write(bcm, 0x0406, 0x0007);
bcm43xx_ilt_write(bcm, 0x3C02, 0x000F);
bcm43xx_ilt_write(bcm, 0x3C03, 0x0014);
break;
default:
assert(0);
}
}
/* Initialize APHY. This is also called for the GPHY in some cases. */
static void bcm43xx_phy_inita(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 tval;
if (phy->type == BCM43xx_PHYTYPE_A) {
bcm43xx_phy_setupa(bcm);
} else {
bcm43xx_phy_setupg(bcm);
if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
bcm43xx_phy_write(bcm, 0x046E, 0x03CF);
return;
}
bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS,
(bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) & 0xF83C) | 0x0340);
bcm43xx_phy_write(bcm, 0x0034, 0x0001);
TODO();//TODO: RSSI AGC
bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) | (1 << 14));
bcm43xx_radio_init2060(bcm);
if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM)
&& ((bcm->board_type == 0x0416) || (bcm->board_type == 0x040A))) {
if (radio->lofcal == 0xFFFF) {
TODO();//TODO: LOF Cal
bcm43xx_radio_set_tx_iq(bcm);
} else
bcm43xx_radio_write16(bcm, 0x001E, radio->lofcal);
}
bcm43xx_phy_write(bcm, 0x007A, 0xF111);
if (phy->savedpctlreg == 0xFFFF) {
bcm43xx_radio_write16(bcm, 0x0019, 0x0000);
bcm43xx_radio_write16(bcm, 0x0017, 0x0020);
tval = bcm43xx_ilt_read(bcm, 0x3001);
if (phy->rev == 1) {
bcm43xx_ilt_write(bcm, 0x3001,
(bcm43xx_ilt_read(bcm, 0x3001) & 0xFF87)
| 0x0058);
} else {
bcm43xx_ilt_write(bcm, 0x3001,
(bcm43xx_ilt_read(bcm, 0x3001) & 0xFFC3)
| 0x002C);
}
bcm43xx_dummy_transmission(bcm);
phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_A_PCTL);
bcm43xx_ilt_write(bcm, 0x3001, tval);
bcm43xx_radio_set_txpower_a(bcm, 0x0018);
}
bcm43xx_radio_clear_tssi(bcm);
}
static void bcm43xx_phy_initb2(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset, val;
bcm43xx_write16(bcm, 0x03EC, 0x3F22);
bcm43xx_phy_write(bcm, 0x0020, 0x301C);
bcm43xx_phy_write(bcm, 0x0026, 0x0000);
bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
bcm43xx_phy_write(bcm, 0x0088, 0x3E00);
val = 0x3C3D;
for (offset = 0x0089; offset < 0x00A7; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
bcm43xx_phy_write(bcm, 0x03E4, 0x3000);
if (radio->channel == 0xFF)
bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
else
bcm43xx_radio_selectchannel(bcm, radio->channel, 0);
if (radio->version != 0x2050) {
bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
}
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
bcm43xx_radio_write16(bcm, 0x007A, 0x000F);
bcm43xx_phy_write(bcm, 0x0038, 0x0677);
bcm43xx_radio_init2050(bcm);
}
bcm43xx_phy_write(bcm, 0x0014, 0x0080);
bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
bcm43xx_phy_write(bcm, 0x0032, 0x00CC);
bcm43xx_phy_write(bcm, 0x0035, 0x07C2);
bcm43xx_phy_lo_b_measure(bcm);
bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
if (radio->version != 0x2050)
bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1000);
bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
if (radio->version != 0x2050)
bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
bcm43xx_phy_init_pctl(bcm);
}
static void bcm43xx_phy_initb4(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset, val;
bcm43xx_write16(bcm, 0x03EC, 0x3F22);
bcm43xx_phy_write(bcm, 0x0020, 0x301C);
bcm43xx_phy_write(bcm, 0x0026, 0x0000);
bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
bcm43xx_phy_write(bcm, 0x0088, 0x3E00);
val = 0x3C3D;
for (offset = 0x0089; offset < 0x00A7; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
bcm43xx_phy_write(bcm, 0x03E4, 0x3000);
if (radio->channel == 0xFF)
bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
else
bcm43xx_radio_selectchannel(bcm, radio->channel, 0);
if (radio->version != 0x2050) {
bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
}
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
bcm43xx_radio_write16(bcm, 0x007A, 0x000F);
bcm43xx_phy_write(bcm, 0x0038, 0x0677);
bcm43xx_radio_init2050(bcm);
}
bcm43xx_phy_write(bcm, 0x0014, 0x0080);
bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x0032, 0x00E0);
bcm43xx_phy_write(bcm, 0x0035, 0x07C2);
bcm43xx_phy_lo_b_measure(bcm);
bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1100);
bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
bcm43xx_calc_nrssi_slope(bcm);
bcm43xx_calc_nrssi_threshold(bcm);
}
bcm43xx_phy_init_pctl(bcm);
}
static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset;
u16 value;
u8 old_channel;
if (phy->analog == 1)
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A)
| 0x0050);
if ((bcm->board_vendor != PCI_VENDOR_ID_BROADCOM) &&
(bcm->board_type != 0x0416)) {
value = 0x2120;
for (offset = 0x00A8 ; offset < 0x00C7; offset++) {
bcm43xx_phy_write(bcm, offset, value);
value += 0x0202;
}
}
bcm43xx_phy_write(bcm, 0x0035,
(bcm43xx_phy_read(bcm, 0x0035) & 0xF0FF)
| 0x0700);
if (radio->version == 0x2050)
bcm43xx_phy_write(bcm, 0x0038, 0x0667);
if (phy->connected) {
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A)
| 0x0020);
bcm43xx_radio_write16(bcm, 0x0051,
bcm43xx_radio_read16(bcm, 0x0051)
| 0x0004);
}
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, 0x0000);
bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) | 0x0100);
bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x2000);
bcm43xx_phy_write(bcm, 0x001C, 0x186A);
bcm43xx_phy_write(bcm, 0x0013, (bcm43xx_phy_read(bcm, 0x0013) & 0x00FF) | 0x1900);
bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFFC0) | 0x0064);
bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D) & 0xFF80) | 0x000A);
}
if (bcm->bad_frames_preempt) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11));
}
if (phy->analog == 1) {
bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
bcm43xx_phy_write(bcm, 0x0021, 0x3763);
bcm43xx_phy_write(bcm, 0x0022, 0x1BC3);
bcm43xx_phy_write(bcm, 0x0023, 0x06F9);
bcm43xx_phy_write(bcm, 0x0024, 0x037E);
} else
bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
bcm43xx_write16(bcm, 0x03EC, 0x3F22);
if (phy->analog == 1)
bcm43xx_phy_write(bcm, 0x0020, 0x3E1C);
else
bcm43xx_phy_write(bcm, 0x0020, 0x301C);
if (phy->analog == 0)
bcm43xx_write16(bcm, 0x03E4, 0x3000);
old_channel = radio->channel;
/* Force to channel 7, even if not supported. */
bcm43xx_radio_selectchannel(bcm, 7, 0);
if (radio->version != 0x2050) {
bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
}
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
}
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0007);
bcm43xx_radio_selectchannel(bcm, old_channel, 0);
bcm43xx_phy_write(bcm, 0x0014, 0x0080);
bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
if (radio->version == 0x2050)
bcm43xx_radio_write16(bcm, 0x005D, 0x000D);
bcm43xx_write16(bcm, 0x03E4, (bcm43xx_read16(bcm, 0x03E4) & 0xFFC0) | 0x0004);
}
static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 offset, val;
u8 old_channel;
bcm43xx_phy_write(bcm, 0x003E, 0x817A);
bcm43xx_radio_write16(bcm, 0x007A,
(bcm43xx_radio_read16(bcm, 0x007A) | 0x0058));
if (radio->revision == 4 ||
radio->revision == 5) {
bcm43xx_radio_write16(bcm, 0x0051, 0x0037);
bcm43xx_radio_write16(bcm, 0x0052, 0x0070);
bcm43xx_radio_write16(bcm, 0x0053, 0x00B3);
bcm43xx_radio_write16(bcm, 0x0054, 0x009B);
bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
bcm43xx_radio_write16(bcm, 0x005B, 0x0088);
bcm43xx_radio_write16(bcm, 0x005D, 0x0088);
bcm43xx_radio_write16(bcm, 0x005E, 0x0088);
bcm43xx_radio_write16(bcm, 0x007D, 0x0088);
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
(bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET)
| 0x00000200));
}
if (radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0051, 0x0000);
bcm43xx_radio_write16(bcm, 0x0052, 0x0040);
bcm43xx_radio_write16(bcm, 0x0053, 0x00B7);
bcm43xx_radio_write16(bcm, 0x0054, 0x0098);
bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
bcm43xx_radio_write16(bcm, 0x005B, 0x006B);
bcm43xx_radio_write16(bcm, 0x005C, 0x000F);
if (bcm->sprom.boardflags & 0x8000) {
bcm43xx_radio_write16(bcm, 0x005D, 0x00FA);
bcm43xx_radio_write16(bcm, 0x005E, 0x00D8);
} else {
bcm43xx_radio_write16(bcm, 0x005D, 0x00F5);
bcm43xx_radio_write16(bcm, 0x005E, 0x00B8);
}
bcm43xx_radio_write16(bcm, 0x0073, 0x0003);
bcm43xx_radio_write16(bcm, 0x007D, 0x00A8);
bcm43xx_radio_write16(bcm, 0x007C, 0x0001);
bcm43xx_radio_write16(bcm, 0x007E, 0x0008);
}
val = 0x1E1F;
for (offset = 0x0088; offset < 0x0098; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
val = 0x3E3F;
for (offset = 0x0098; offset < 0x00A8; offset++) {
bcm43xx_phy_write(bcm, offset, val);
val -= 0x0202;
}
val = 0x2120;
for (offset = 0x00A8; offset < 0x00C8; offset++) {
bcm43xx_phy_write(bcm, offset, (val & 0x3F3F));
val += 0x0202;
}
if (phy->type == BCM43xx_PHYTYPE_G) {
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0020);
bcm43xx_radio_write16(bcm, 0x0051,
bcm43xx_radio_read16(bcm, 0x0051) | 0x0004);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) | 0x0100);
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B) | 0x2000);
bcm43xx_phy_write(bcm, 0x5B, 0x0000);
bcm43xx_phy_write(bcm, 0x5C, 0x0000);
}
old_channel = radio->channel;
if (old_channel >= 8)
bcm43xx_radio_selectchannel(bcm, 1, 0);
else
bcm43xx_radio_selectchannel(bcm, 13, 0);
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
udelay(40);
if (radio->revision < 6 || radio-> revision == 8) {
bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C)
| 0x0002));
bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
}
if (radio->revision <= 2) {
bcm43xx_radio_write16(bcm, 0x007C, 0x0020);
bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
}
bcm43xx_radio_write16(bcm, 0x007A,
(bcm43xx_radio_read16(bcm, 0x007A) & 0x00F8) | 0x0007);
bcm43xx_radio_selectchannel(bcm, old_channel, 0);
bcm43xx_phy_write(bcm, 0x0014, 0x0200);
if (radio->revision >= 6)
bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
else
bcm43xx_phy_write(bcm, 0x002A, 0x8AC0);
bcm43xx_phy_write(bcm, 0x0038, 0x0668);
bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
if (radio->revision <= 5)
bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D)
& 0xFF80) | 0x0003);
if (radio->revision <= 2)
bcm43xx_radio_write16(bcm, 0x005D, 0x000D);
if (phy->analog == 4){
bcm43xx_write16(bcm, 0x03E4, 0x0009);
bcm43xx_phy_write(bcm, 0x61, bcm43xx_phy_read(bcm, 0x61) & 0xFFF);
} else {
bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004);
}
if (phy->type == BCM43xx_PHYTYPE_G)
bcm43xx_write16(bcm, 0x03E6, 0x0);
if (phy->type == BCM43xx_PHYTYPE_B) {
bcm43xx_write16(bcm, 0x03E6, 0x8140);
bcm43xx_phy_write(bcm, 0x0016, 0x0410);
bcm43xx_phy_write(bcm, 0x0017, 0x0820);
bcm43xx_phy_write(bcm, 0x0062, 0x0007);
bcm43xx_radio_init2050(bcm);
bcm43xx_phy_lo_g_measure(bcm);
if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
bcm43xx_calc_nrssi_slope(bcm);
bcm43xx_calc_nrssi_threshold(bcm);
}
bcm43xx_phy_init_pctl(bcm);
}
}
static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 backup_phy[15] = {0};
u16 backup_radio[3];
u16 backup_bband;
u16 i;
u16 loop1_cnt, loop1_done, loop1_omitted;
u16 loop2_done;
backup_phy[0] = bcm43xx_phy_read(bcm, 0x0429);
backup_phy[1] = bcm43xx_phy_read(bcm, 0x0001);
backup_phy[2] = bcm43xx_phy_read(bcm, 0x0811);
backup_phy[3] = bcm43xx_phy_read(bcm, 0x0812);
if (phy->rev != 1) {
backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814);
backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815);
}
backup_phy[6] = bcm43xx_phy_read(bcm, 0x005A);
backup_phy[7] = bcm43xx_phy_read(bcm, 0x0059);
backup_phy[8] = bcm43xx_phy_read(bcm, 0x0058);
backup_phy[9] = bcm43xx_phy_read(bcm, 0x000A);
backup_phy[10] = bcm43xx_phy_read(bcm, 0x0003);
backup_phy[11] = bcm43xx_phy_read(bcm, 0x080F);
backup_phy[12] = bcm43xx_phy_read(bcm, 0x0810);
backup_phy[13] = bcm43xx_phy_read(bcm, 0x002B);
backup_phy[14] = bcm43xx_phy_read(bcm, 0x0015);
bcm43xx_phy_read(bcm, 0x002D); /* dummy read */
backup_bband = radio->baseband_atten;
backup_radio[0] = bcm43xx_radio_read16(bcm, 0x0052);
backup_radio[1] = bcm43xx_radio_read16(bcm, 0x0043);
backup_radio[2] = bcm43xx_radio_read16(bcm, 0x007A);
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) & 0x3FFF);
bcm43xx_phy_write(bcm, 0x0001,
bcm43xx_phy_read(bcm, 0x0001) & 0x8000);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0002);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xFFFD);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0001);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xFFFE);
if (phy->rev != 1) {
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0001);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE);
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0002);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD);
}
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) | 0x000C);
bcm43xx_phy_write(bcm, 0x0811,
(bcm43xx_phy_read(bcm, 0x0811)
& 0xFFCF) | 0x0030);
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812)
& 0xFFCF) | 0x0010);
bcm43xx_phy_write(bcm, 0x005A, 0x0780);
bcm43xx_phy_write(bcm, 0x0059, 0xC810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
if (phy->analog == 0) {
bcm43xx_phy_write(bcm, 0x0003, 0x0122);
} else {
bcm43xx_phy_write(bcm, 0x000A,
bcm43xx_phy_read(bcm, 0x000A)
| 0x2000);
}
if (phy->rev != 1) {
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0004);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB);
}
bcm43xx_phy_write(bcm, 0x0003,
(bcm43xx_phy_read(bcm, 0x0003)
& 0xFF9F) | 0x0040);
if (radio->version == 0x2050 && radio->revision == 2) {
bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
bcm43xx_radio_write16(bcm, 0x0043,
(bcm43xx_radio_read16(bcm, 0x0043)
& 0xFFF0) | 0x0009);
loop1_cnt = 9;
} else if (radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0043, 0x000F);
loop1_cnt = 15;
} else
loop1_cnt = 0;
bcm43xx_phy_set_baseband_attenuation(bcm, 11);
if (phy->rev >= 3)
bcm43xx_phy_write(bcm, 0x080F, 0xC020);
else
bcm43xx_phy_write(bcm, 0x080F, 0x8020);
bcm43xx_phy_write(bcm, 0x0810, 0x0000);
bcm43xx_phy_write(bcm, 0x002B,
(bcm43xx_phy_read(bcm, 0x002B)
& 0xFFC0) | 0x0001);
bcm43xx_phy_write(bcm, 0x002B,
(bcm43xx_phy_read(bcm, 0x002B)
& 0xC0FF) | 0x0800);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0100);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xCFFF);
if (bcm->sprom.boardflags & BCM43xx_BFL_EXTLNA) {
if (phy->rev >= 7) {
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811)
| 0x0800);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812)
| 0x8000);
}
}
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A)
& 0x00F7);
for (i = 0; i < loop1_cnt; i++) {
bcm43xx_radio_write16(bcm, 0x0043, loop1_cnt);
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812)
& 0xF0FF) | (i << 8));
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xA000);
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xF000);
udelay(20);
if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC)
break;
}
loop1_done = i;
loop1_omitted = loop1_cnt - loop1_done;
loop2_done = 0;
if (loop1_done >= 8) {
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812)
| 0x0030);
for (i = loop1_done - 8; i < 16; i++) {
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812)
& 0xF0FF) | (i << 8));
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xA000);
bcm43xx_phy_write(bcm, 0x0015,
(bcm43xx_phy_read(bcm, 0x0015)
& 0x0FFF) | 0xF000);
udelay(20);
if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC)
break;
}
}
if (phy->rev != 1) {
bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]);
bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]);
}
bcm43xx_phy_write(bcm, 0x005A, backup_phy[6]);
bcm43xx_phy_write(bcm, 0x0059, backup_phy[7]);
bcm43xx_phy_write(bcm, 0x0058, backup_phy[8]);
bcm43xx_phy_write(bcm, 0x000A, backup_phy[9]);
bcm43xx_phy_write(bcm, 0x0003, backup_phy[10]);
bcm43xx_phy_write(bcm, 0x080F, backup_phy[11]);
bcm43xx_phy_write(bcm, 0x0810, backup_phy[12]);
bcm43xx_phy_write(bcm, 0x002B, backup_phy[13]);
bcm43xx_phy_write(bcm, 0x0015, backup_phy[14]);
bcm43xx_phy_set_baseband_attenuation(bcm, backup_bband);
bcm43xx_radio_write16(bcm, 0x0052, backup_radio[0]);
bcm43xx_radio_write16(bcm, 0x0043, backup_radio[1]);
bcm43xx_radio_write16(bcm, 0x007A, backup_radio[2]);
bcm43xx_phy_write(bcm, 0x0811, backup_phy[2] | 0x0003);
udelay(10);
bcm43xx_phy_write(bcm, 0x0811, backup_phy[2]);
bcm43xx_phy_write(bcm, 0x0812, backup_phy[3]);
bcm43xx_phy_write(bcm, 0x0429, backup_phy[0]);
bcm43xx_phy_write(bcm, 0x0001, backup_phy[1]);
phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11;
phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2;
}
static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 tmp;
if (phy->rev == 1)
bcm43xx_phy_initb5(bcm);
else
bcm43xx_phy_initb6(bcm);
if (phy->rev >= 2 || phy->connected)
bcm43xx_phy_inita(bcm);
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0814, 0x0000);
bcm43xx_phy_write(bcm, 0x0815, 0x0000);
}
if (phy->rev == 2) {
bcm43xx_phy_write(bcm, 0x0811, 0x0000);
bcm43xx_phy_write(bcm, 0x0015, 0x00C0);
}
if (phy->rev > 5) {
bcm43xx_phy_write(bcm, 0x0811, 0x0400);
bcm43xx_phy_write(bcm, 0x0015, 0x00C0);
}
if (phy->rev >= 2 && phy->connected) {
tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF;
if (tmp ==3 || tmp == 5) {
bcm43xx_phy_write(bcm, 0x04C2, 0x1816);
bcm43xx_phy_write(bcm, 0x04C3, 0x8006);
if (tmp == 5) {
bcm43xx_phy_write(bcm, 0x04CC,
(bcm43xx_phy_read(bcm, 0x04CC)
& 0x00FF) | 0x1F00);
}
}
bcm43xx_phy_write(bcm, 0x047E, 0x0078);
}
if (radio->revision == 8) {
bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0080);
bcm43xx_phy_write(bcm, 0x043E, bcm43xx_phy_read(bcm, 0x043E) | 0x0004);
}
if (phy->rev >= 2 && phy->connected)
bcm43xx_calc_loopback_gain(bcm);
if (radio->revision != 8) {
if (radio->initval == 0xFFFF)
radio->initval = bcm43xx_radio_init2050(bcm);
else
bcm43xx_radio_write16(bcm, 0x0078, radio->initval);
}
if (radio->txctl2 == 0xFFFF) {
bcm43xx_phy_lo_g_measure(bcm);
} else {
if (radio->version == 0x2050 && radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0052,
(radio->txctl1 << 4) | radio->txctl2);
} else {
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052)
& 0xFFF0) | radio->txctl1);
}
if (phy->rev >= 6) {
bcm43xx_phy_write(bcm, 0x0036,
(bcm43xx_phy_read(bcm, 0x0036)
& 0x0FFF) | (radio->txctl2 << 12));
}
if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
bcm43xx_phy_write(bcm, 0x002E, 0x8075);
else
bcm43xx_phy_write(bcm, 0x002E, 0x807F);
if (phy->rev < 2)
bcm43xx_phy_write(bcm, 0x002F, 0x0101);
else
bcm43xx_phy_write(bcm, 0x002F, 0x0202);
}
if (phy->connected || phy->rev >= 2) {
bcm43xx_phy_lo_adjust(bcm, 0);
bcm43xx_phy_write(bcm, 0x080F, 0x8078);
}
if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) {
/* The specs state to update the NRSSI LT with
* the value 0x7FFFFFFF here. I think that is some weird
* compiler optimization in the original driver.
* Essentially, what we do here is resetting all NRSSI LT
* entries to -32 (see the limit_value() in nrssi_hw_update())
*/
bcm43xx_nrssi_hw_update(bcm, 0xFFFF);
bcm43xx_calc_nrssi_threshold(bcm);
} else if (phy->connected || phy->rev >= 2) {
if (radio->nrssi[0] == -1000) {
assert(radio->nrssi[1] == -1000);
bcm43xx_calc_nrssi_slope(bcm);
} else {
assert(radio->nrssi[1] != -1000);
bcm43xx_calc_nrssi_threshold(bcm);
}
}
if (radio->revision == 8)
bcm43xx_phy_write(bcm, 0x0805, 0x3230);
bcm43xx_phy_init_pctl(bcm);
if (bcm->chip_id == 0x4306 && bcm->chip_package == 2) {
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF);
bcm43xx_phy_write(bcm, 0x04C3,
bcm43xx_phy_read(bcm, 0x04C3) & 0x7FFF);
}
}
static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm)
{
int i;
u16 ret = 0;
unsigned long flags;
local_irq_save(flags);
for (i = 0; i < 10; i++){
bcm43xx_phy_write(bcm, 0x0015, 0xAFA0);
udelay(1);
bcm43xx_phy_write(bcm, 0x0015, 0xEFA0);
udelay(10);
bcm43xx_phy_write(bcm, 0x0015, 0xFFA0);
udelay(40);
ret += bcm43xx_phy_read(bcm, 0x002C);
}
local_irq_restore(flags);
bcm43xx_voluntary_preempt();
return ret;
}
void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 regstack[12] = { 0 };
u16 mls;
u16 fval;
int i, j;
regstack[0] = bcm43xx_phy_read(bcm, 0x0015);
regstack[1] = bcm43xx_radio_read16(bcm, 0x0052) & 0xFFF0;
if (radio->version == 0x2053) {
regstack[2] = bcm43xx_phy_read(bcm, 0x000A);
regstack[3] = bcm43xx_phy_read(bcm, 0x002A);
regstack[4] = bcm43xx_phy_read(bcm, 0x0035);
regstack[5] = bcm43xx_phy_read(bcm, 0x0003);
regstack[6] = bcm43xx_phy_read(bcm, 0x0001);
regstack[7] = bcm43xx_phy_read(bcm, 0x0030);
regstack[8] = bcm43xx_radio_read16(bcm, 0x0043);
regstack[9] = bcm43xx_radio_read16(bcm, 0x007A);
regstack[10] = bcm43xx_read16(bcm, 0x03EC);
regstack[11] = bcm43xx_radio_read16(bcm, 0x0052) & 0x00F0;
bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
bcm43xx_write16(bcm, 0x03EC, 0x3F3F);
bcm43xx_phy_write(bcm, 0x0035, regstack[4] & 0xFF7F);
bcm43xx_radio_write16(bcm, 0x007A, regstack[9] & 0xFFF0);
}
bcm43xx_phy_write(bcm, 0x0015, 0xB000);
bcm43xx_phy_write(bcm, 0x002B, 0x0004);
if (radio->version == 0x2053) {
bcm43xx_phy_write(bcm, 0x002B, 0x0203);
bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
}
phy->minlowsig[0] = 0xFFFF;
for (i = 0; i < 4; i++) {
bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i);
bcm43xx_phy_lo_b_r15_loop(bcm);
}
for (i = 0; i < 10; i++) {
bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i);
mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10;
if (mls < phy->minlowsig[0]) {
phy->minlowsig[0] = mls;
phy->minlowsigpos[0] = i;
}
}
bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | phy->minlowsigpos[0]);
phy->minlowsig[1] = 0xFFFF;
for (i = -4; i < 5; i += 2) {
for (j = -4; j < 5; j += 2) {
if (j < 0)
fval = (0x0100 * i) + j + 0x0100;
else
fval = (0x0100 * i) + j;
bcm43xx_phy_write(bcm, 0x002F, fval);
mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10;
if (mls < phy->minlowsig[1]) {
phy->minlowsig[1] = mls;
phy->minlowsigpos[1] = fval;
}
}
}
phy->minlowsigpos[1] += 0x0101;
bcm43xx_phy_write(bcm, 0x002F, phy->minlowsigpos[1]);
if (radio->version == 0x2053) {
bcm43xx_phy_write(bcm, 0x000A, regstack[2]);
bcm43xx_phy_write(bcm, 0x002A, regstack[3]);
bcm43xx_phy_write(bcm, 0x0035, regstack[4]);
bcm43xx_phy_write(bcm, 0x0003, regstack[5]);
bcm43xx_phy_write(bcm, 0x0001, regstack[6]);
bcm43xx_phy_write(bcm, 0x0030, regstack[7]);
bcm43xx_radio_write16(bcm, 0x0043, regstack[8]);
bcm43xx_radio_write16(bcm, 0x007A, regstack[9]);
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052) & 0x000F)
| regstack[11]);
bcm43xx_write16(bcm, 0x03EC, regstack[10]);
}
bcm43xx_phy_write(bcm, 0x0015, regstack[0]);
}
static inline
u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 ret;
unsigned long flags;
local_irq_save(flags);
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x15, 0xE300);
control <<= 8;
bcm43xx_phy_write(bcm, 0x0812, control | 0x00B0);
udelay(5);
bcm43xx_phy_write(bcm, 0x0812, control | 0x00B2);
udelay(2);
bcm43xx_phy_write(bcm, 0x0812, control | 0x00B3);
udelay(4);
bcm43xx_phy_write(bcm, 0x0015, 0xF300);
udelay(8);
} else {
bcm43xx_phy_write(bcm, 0x0015, control | 0xEFA0);
udelay(2);
bcm43xx_phy_write(bcm, 0x0015, control | 0xEFE0);
udelay(4);
bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0);
udelay(8);
}
ret = bcm43xx_phy_read(bcm, 0x002D);
local_irq_restore(flags);
bcm43xx_voluntary_preempt();
return ret;
}
static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control)
{
int i;
u32 ret = 0;
for (i = 0; i < 8; i++)
ret += bcm43xx_phy_lo_g_deviation_subval(bcm, control);
return ret;
}
/* Write the LocalOscillator CONTROL */
static inline
void bcm43xx_lo_write(struct bcm43xx_private *bcm,
struct bcm43xx_lopair *pair)
{
u16 value;
value = (u8)(pair->low);
value |= ((u8)(pair->high)) << 8;
#ifdef CONFIG_BCM43XX_DEBUG
/* Sanity check. */
if (pair->low < -8 || pair->low > 8 ||
pair->high < -8 || pair->high > 8) {
printk(KERN_WARNING PFX
"WARNING: Writing invalid LOpair "
"(low: %d, high: %d, index: %lu)\n",
pair->low, pair->high,
(unsigned long)(pair - bcm43xx_current_phy(bcm)->_lo_pairs));
dump_stack();
}
#endif
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, value);
}
static inline
struct bcm43xx_lopair * bcm43xx_find_lopair(struct bcm43xx_private *bcm,
u16 baseband_attenuation,
u16 radio_attenuation,
u16 tx)
{
static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 };
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
if (baseband_attenuation > 6)
baseband_attenuation = 6;
assert(radio_attenuation < 10);
if (tx == 3) {
return bcm43xx_get_lopair(phy,
radio_attenuation,
baseband_attenuation);
}
return bcm43xx_get_lopair(phy, dict[radio_attenuation], baseband_attenuation);
}
static inline
struct bcm43xx_lopair * bcm43xx_current_lopair(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
return bcm43xx_find_lopair(bcm,
radio->baseband_atten,
radio->radio_atten,
radio->txctl1);
}
/* Adjust B/G LO */
void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed)
{
struct bcm43xx_lopair *pair;
if (fixed) {
/* Use fixed values. Only for initialization. */
pair = bcm43xx_find_lopair(bcm, 2, 3, 0);
} else
pair = bcm43xx_current_lopair(bcm);
bcm43xx_lo_write(bcm, pair);
}
static void bcm43xx_phy_lo_g_measure_txctl2(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 txctl2 = 0, i;
u32 smallest, tmp;
bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
udelay(10);
smallest = bcm43xx_phy_lo_g_singledeviation(bcm, 0);
for (i = 0; i < 16; i++) {
bcm43xx_radio_write16(bcm, 0x0052, i);
udelay(10);
tmp = bcm43xx_phy_lo_g_singledeviation(bcm, 0);
if (tmp < smallest) {
smallest = tmp;
txctl2 = i;
}
}
radio->txctl2 = txctl2;
}
static
void bcm43xx_phy_lo_g_state(struct bcm43xx_private *bcm,
const struct bcm43xx_lopair *in_pair,
struct bcm43xx_lopair *out_pair,
u16 r27)
{
static const struct bcm43xx_lopair transitions[8] = {
{ .high = 1, .low = 1, },
{ .high = 1, .low = 0, },
{ .high = 1, .low = -1, },
{ .high = 0, .low = -1, },
{ .high = -1, .low = -1, },
{ .high = -1, .low = 0, },
{ .high = -1, .low = 1, },
{ .high = 0, .low = 1, },
};
struct bcm43xx_lopair lowest_transition = {
.high = in_pair->high,
.low = in_pair->low,
};
struct bcm43xx_lopair tmp_pair;
struct bcm43xx_lopair transition;
int i = 12;
int state = 0;
int found_lower;
int j, begin, end;
u32 lowest_deviation;
u32 tmp;
/* Note that in_pair and out_pair can point to the same pair. Be careful. */
bcm43xx_lo_write(bcm, &lowest_transition);
lowest_deviation = bcm43xx_phy_lo_g_singledeviation(bcm, r27);
do {
found_lower = 0;
assert(state >= 0 && state <= 8);
if (state == 0) {
begin = 1;
end = 8;
} else if (state % 2 == 0) {
begin = state - 1;
end = state + 1;
} else {
begin = state - 2;
end = state + 2;
}
if (begin < 1)
begin += 8;
if (end > 8)
end -= 8;
j = begin;
tmp_pair.high = lowest_transition.high;
tmp_pair.low = lowest_transition.low;
while (1) {
assert(j >= 1 && j <= 8);
transition.high = tmp_pair.high + transitions[j - 1].high;
transition.low = tmp_pair.low + transitions[j - 1].low;
if ((abs(transition.low) < 9) && (abs(transition.high) < 9)) {
bcm43xx_lo_write(bcm, &transition);
tmp = bcm43xx_phy_lo_g_singledeviation(bcm, r27);
if (tmp < lowest_deviation) {
lowest_deviation = tmp;
state = j;
found_lower = 1;
lowest_transition.high = transition.high;
lowest_transition.low = transition.low;
}
}
if (j == end)
break;
if (j == 8)
j = 1;
else
j++;
}
} while (i-- && found_lower);
out_pair->high = lowest_transition.high;
out_pair->low = lowest_transition.low;
}
/* Set the baseband attenuation value on chip. */
void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm,
u16 baseband_attenuation)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 value;
if (phy->analog == 0) {
value = (bcm43xx_read16(bcm, 0x03E6) & 0xFFF0);
value |= (baseband_attenuation & 0x000F);
bcm43xx_write16(bcm, 0x03E6, value);
return;
}
if (phy->analog > 1) {
value = bcm43xx_phy_read(bcm, 0x0060) & ~0x003C;
value |= (baseband_attenuation << 2) & 0x003C;
} else {
value = bcm43xx_phy_read(bcm, 0x0060) & ~0x0078;
value |= (baseband_attenuation << 3) & 0x0078;
}
bcm43xx_phy_write(bcm, 0x0060, value);
}
/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */
void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm)
{
static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 };
const int is_initializing = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZING);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 h, i, oldi = 0, j;
struct bcm43xx_lopair control;
struct bcm43xx_lopair *tmp_control;
u16 tmp;
u16 regstack[16] = { 0 };
u8 oldchannel;
//XXX: What are these?
u8 r27 = 0, r31;
oldchannel = radio->channel;
/* Setup */
if (phy->connected) {
regstack[0] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS);
regstack[1] = bcm43xx_phy_read(bcm, 0x0802);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF);
bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC);
}
regstack[3] = bcm43xx_read16(bcm, 0x03E2);
bcm43xx_write16(bcm, 0x03E2, regstack[3] | 0x8000);
regstack[4] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
regstack[5] = bcm43xx_phy_read(bcm, 0x15);
regstack[6] = bcm43xx_phy_read(bcm, 0x2A);
regstack[7] = bcm43xx_phy_read(bcm, 0x35);
regstack[8] = bcm43xx_phy_read(bcm, 0x60);
regstack[9] = bcm43xx_radio_read16(bcm, 0x43);
regstack[10] = bcm43xx_radio_read16(bcm, 0x7A);
regstack[11] = bcm43xx_radio_read16(bcm, 0x52);
if (phy->connected) {
regstack[12] = bcm43xx_phy_read(bcm, 0x0811);
regstack[13] = bcm43xx_phy_read(bcm, 0x0812);
regstack[14] = bcm43xx_phy_read(bcm, 0x0814);
regstack[15] = bcm43xx_phy_read(bcm, 0x0815);
}
bcm43xx_radio_selectchannel(bcm, 6, 0);
if (phy->connected) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF);
bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC);
bcm43xx_dummy_transmission(bcm);
}
bcm43xx_radio_write16(bcm, 0x0043, 0x0006);
bcm43xx_phy_set_baseband_attenuation(bcm, 2);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x0000);
bcm43xx_phy_write(bcm, 0x002E, 0x007F);
bcm43xx_phy_write(bcm, 0x080F, 0x0078);
bcm43xx_phy_write(bcm, 0x0035, regstack[7] & ~(1 << 7));
bcm43xx_radio_write16(bcm, 0x007A, regstack[10] & 0xFFF0);
bcm43xx_phy_write(bcm, 0x002B, 0x0203);
bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x0814, regstack[14] | 0x0003);
bcm43xx_phy_write(bcm, 0x0815, regstack[15] & 0xFFFC);
bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
bcm43xx_phy_write(bcm, 0x0812, 0x00B2);
}
if (is_initializing)
bcm43xx_phy_lo_g_measure_txctl2(bcm);
bcm43xx_phy_write(bcm, 0x080F, 0x8078);
/* Measure */
control.low = 0;
control.high = 0;
for (h = 0; h < 10; h++) {
/* Loop over each possible RadioAttenuation (0-9) */
i = pairorder[h];
if (is_initializing) {
if (i == 3) {
control.low = 0;
control.high = 0;
} else if (((i % 2 == 1) && (oldi % 2 == 1)) ||
((i % 2 == 0) && (oldi % 2 == 0))) {
tmp_control = bcm43xx_get_lopair(phy, oldi, 0);
memcpy(&control, tmp_control, sizeof(control));
} else {
tmp_control = bcm43xx_get_lopair(phy, 3, 0);
memcpy(&control, tmp_control, sizeof(control));
}
}
/* Loop over each possible BasebandAttenuation/2 */
for (j = 0; j < 4; j++) {
if (is_initializing) {
tmp = i * 2 + j;
r27 = 0;
r31 = 0;
if (tmp > 14) {
r31 = 1;
if (tmp > 17)
r27 = 1;
if (tmp > 19)
r27 = 2;
}
} else {
tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
if (!tmp_control->used)
continue;
memcpy(&control, tmp_control, sizeof(control));
r27 = 3;
r31 = 0;
}
bcm43xx_radio_write16(bcm, 0x43, i);
bcm43xx_radio_write16(bcm, 0x52, radio->txctl2);
udelay(10);
bcm43xx_voluntary_preempt();
bcm43xx_phy_set_baseband_attenuation(bcm, j * 2);
tmp = (regstack[10] & 0xFFF0);
if (r31)
tmp |= 0x0008;
bcm43xx_radio_write16(bcm, 0x007A, tmp);
tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27);
}
oldi = i;
}
/* Loop over each possible RadioAttenuation (10-13) */
for (i = 10; i < 14; i++) {
/* Loop over each possible BasebandAttenuation/2 */
for (j = 0; j < 4; j++) {
if (is_initializing) {
tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2);
memcpy(&control, tmp_control, sizeof(control));
tmp = (i - 9) * 2 + j - 5;//FIXME: This is wrong, as the following if statement can never trigger.
r27 = 0;
r31 = 0;
if (tmp > 14) {
r31 = 1;
if (tmp > 17)
r27 = 1;
if (tmp > 19)
r27 = 2;
}
} else {
tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2);
if (!tmp_control->used)
continue;
memcpy(&control, tmp_control, sizeof(control));
r27 = 3;
r31 = 0;
}
bcm43xx_radio_write16(bcm, 0x43, i - 9);
bcm43xx_radio_write16(bcm, 0x52,
radio->txctl2
| (3/*txctl1*/ << 4));//FIXME: shouldn't txctl1 be zero here and 3 in the loop above?
udelay(10);
bcm43xx_voluntary_preempt();
bcm43xx_phy_set_baseband_attenuation(bcm, j * 2);
tmp = (regstack[10] & 0xFFF0);
if (r31)
tmp |= 0x0008;
bcm43xx_radio_write16(bcm, 0x7A, tmp);
tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27);
}
}
/* Restoration */
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x0015, 0xE300);
bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA0);
udelay(5);
bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA2);
udelay(2);
bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA3);
bcm43xx_voluntary_preempt();
} else
bcm43xx_phy_write(bcm, 0x0015, r27 | 0xEFA0);
bcm43xx_phy_lo_adjust(bcm, is_initializing);
bcm43xx_phy_write(bcm, 0x002E, 0x807F);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x002F, 0x0202);
else
bcm43xx_phy_write(bcm, 0x002F, 0x0101);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, regstack[4]);
bcm43xx_phy_write(bcm, 0x0015, regstack[5]);
bcm43xx_phy_write(bcm, 0x002A, regstack[6]);
bcm43xx_phy_write(bcm, 0x0035, regstack[7]);
bcm43xx_phy_write(bcm, 0x0060, regstack[8]);
bcm43xx_radio_write16(bcm, 0x0043, regstack[9]);
bcm43xx_radio_write16(bcm, 0x007A, regstack[10]);
regstack[11] &= 0x00F0;
regstack[11] |= (bcm43xx_radio_read16(bcm, 0x52) & 0x000F);
bcm43xx_radio_write16(bcm, 0x52, regstack[11]);
bcm43xx_write16(bcm, 0x03E2, regstack[3]);
if (phy->connected) {
bcm43xx_phy_write(bcm, 0x0811, regstack[12]);
bcm43xx_phy_write(bcm, 0x0812, regstack[13]);
bcm43xx_phy_write(bcm, 0x0814, regstack[14]);
bcm43xx_phy_write(bcm, 0x0815, regstack[15]);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0]);
bcm43xx_phy_write(bcm, 0x0802, regstack[1]);
}
bcm43xx_radio_selectchannel(bcm, oldchannel, 1);
#ifdef CONFIG_BCM43XX_DEBUG
{
/* Sanity check for all lopairs. */
for (i = 0; i < BCM43xx_LO_COUNT; i++) {
tmp_control = phy->_lo_pairs + i;
if (tmp_control->low < -8 || tmp_control->low > 8 ||
tmp_control->high < -8 || tmp_control->high > 8) {
printk(KERN_WARNING PFX
"WARNING: Invalid LOpair (low: %d, high: %d, index: %d)\n",
tmp_control->low, tmp_control->high, i);
}
}
}
#endif /* CONFIG_BCM43XX_DEBUG */
}
static
void bcm43xx_phy_lo_mark_current_used(struct bcm43xx_private *bcm)
{
struct bcm43xx_lopair *pair;
pair = bcm43xx_current_lopair(bcm);
pair->used = 1;
}
void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_lopair *pair;
int i;
for (i = 0; i < BCM43xx_LO_COUNT; i++) {
pair = phy->_lo_pairs + i;
pair->used = 0;
}
}
/* http://bcm-specs.sipsolutions.net/EstimatePowerOut
* This function converts a TSSI value to dBm in Q5.2
*/
static s8 bcm43xx_phy_estimate_power_out(struct bcm43xx_private *bcm, s8 tssi)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
s8 dbm = 0;
s32 tmp;
tmp = phy->idle_tssi;
tmp += tssi;
tmp -= phy->savedpctlreg;
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
tmp += 0x80;
tmp = limit_value(tmp, 0x00, 0xFF);
dbm = phy->tssi2dbm[tmp];
TODO(); //TODO: There's a FIXME on the specs
break;
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G:
tmp = limit_value(tmp, 0x00, 0x3F);
dbm = phy->tssi2dbm[tmp];
break;
default:
assert(0);
}
return dbm;
}
/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
if (phy->savedpctlreg == 0xFFFF)
return;
if ((bcm->board_type == 0x0416) &&
(bcm->board_vendor == PCI_VENDOR_ID_BROADCOM))
return;
switch (phy->type) {
case BCM43xx_PHYTYPE_A: {
TODO(); //TODO: Nothing for A PHYs yet :-/
break;
}
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G: {
u16 tmp;
u16 txpower;
s8 v0, v1, v2, v3;
s8 average;
u8 max_pwr;
s16 desired_pwr, estimated_pwr, pwr_adjust;
s16 radio_att_delta, baseband_att_delta;
s16 radio_attenuation, baseband_attenuation;
unsigned long phylock_flags;
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0058);
v0 = (s8)(tmp & 0x00FF);
v1 = (s8)((tmp & 0xFF00) >> 8);
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005A);
v2 = (s8)(tmp & 0x00FF);
v3 = (s8)((tmp & 0xFF00) >> 8);
tmp = 0;
if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) {
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0070);
v0 = (s8)(tmp & 0x00FF);
v1 = (s8)((tmp & 0xFF00) >> 8);
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0072);
v2 = (s8)(tmp & 0x00FF);
v3 = (s8)((tmp & 0xFF00) >> 8);
if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F)
return;
v0 = (v0 + 0x20) & 0x3F;
v1 = (v1 + 0x20) & 0x3F;
v2 = (v2 + 0x20) & 0x3F;
v3 = (v3 + 0x20) & 0x3F;
tmp = 1;
}
bcm43xx_radio_clear_tssi(bcm);
average = (v0 + v1 + v2 + v3 + 2) / 4;
if (tmp && (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005E) & 0x8))
average -= 13;
estimated_pwr = bcm43xx_phy_estimate_power_out(bcm, average);
max_pwr = bcm->sprom.maxpower_bgphy;
if ((bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) &&
(phy->type == BCM43xx_PHYTYPE_G))
max_pwr -= 0x3;
/*TODO:
max_pwr = min(REG - bcm->sprom.antennagain_bgphy - 0x6, max_pwr)
where REG is the max power as per the regulatory domain
*/
desired_pwr = limit_value(radio->txpower_desired, 0, max_pwr);
/* Check if we need to adjust the current power. */
pwr_adjust = desired_pwr - estimated_pwr;
radio_att_delta = -(pwr_adjust + 7) >> 3;
baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta);
if ((radio_att_delta == 0) && (baseband_att_delta == 0)) {
bcm43xx_phy_lo_mark_current_used(bcm);
return;
}
/* Calculate the new attenuation values. */
baseband_attenuation = radio->baseband_atten;
baseband_attenuation += baseband_att_delta;
radio_attenuation = radio->radio_atten;
radio_attenuation += radio_att_delta;
/* Get baseband and radio attenuation values into their permitted ranges.
* baseband 0-11, radio 0-9.
* Radio attenuation affects power level 4 times as much as baseband.
*/
if (radio_attenuation < 0) {
baseband_attenuation -= (4 * -radio_attenuation);
radio_attenuation = 0;
} else if (radio_attenuation > 9) {
baseband_attenuation += (4 * (radio_attenuation - 9));
radio_attenuation = 9;
} else {
while (baseband_attenuation < 0 && radio_attenuation > 0) {
baseband_attenuation += 4;
radio_attenuation--;
}
while (baseband_attenuation > 11 && radio_attenuation < 9) {
baseband_attenuation -= 4;
radio_attenuation++;
}
}
baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
txpower = radio->txctl1;
if ((radio->version == 0x2050) && (radio->revision == 2)) {
if (radio_attenuation <= 1) {
if (txpower == 0) {
txpower = 3;
radio_attenuation += 2;
baseband_attenuation += 2;
} else if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) {
baseband_attenuation += 4 * (radio_attenuation - 2);
radio_attenuation = 2;
}
} else if (radio_attenuation > 4 && txpower != 0) {
txpower = 0;
if (baseband_attenuation < 3) {
radio_attenuation -= 3;
baseband_attenuation += 2;
} else {
radio_attenuation -= 2;
baseband_attenuation -= 2;
}
}
}
radio->txctl1 = txpower;
baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
radio_attenuation = limit_value(radio_attenuation, 0, 9);
bcm43xx_phy_lock(bcm, phylock_flags);
bcm43xx_radio_lock(bcm);
bcm43xx_radio_set_txpower_bg(bcm, baseband_attenuation,
radio_attenuation, txpower);
bcm43xx_phy_lo_mark_current_used(bcm);
bcm43xx_radio_unlock(bcm);
bcm43xx_phy_unlock(bcm, phylock_flags);
break;
}
default:
assert(0);
}
}
static inline
s32 bcm43xx_tssi2dbm_ad(s32 num, s32 den)
{
if (num < 0)
return num/den;
else
return (num+den/2)/den;
}
static inline
s8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2)
{
s32 m1, m2, f = 256, q, delta;
s8 i = 0;
m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32);
m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1);
do {
if (i > 15)
return -EINVAL;
q = bcm43xx_tssi2dbm_ad(f * 4096 -
bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048);
delta = abs(q - f);
f = q;
i++;
} while (delta >= 2);
entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128);
return 0;
}
/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */
int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
s16 pab0, pab1, pab2;
u8 idx;
s8 *dyn_tssi2dbm;
if (phy->type == BCM43xx_PHYTYPE_A) {
pab0 = (s16)(bcm->sprom.pa1b0);
pab1 = (s16)(bcm->sprom.pa1b1);
pab2 = (s16)(bcm->sprom.pa1b2);
} else {
pab0 = (s16)(bcm->sprom.pa0b0);
pab1 = (s16)(bcm->sprom.pa0b1);
pab2 = (s16)(bcm->sprom.pa0b2);
}
if ((bcm->chip_id == 0x4301) && (radio->version != 0x2050)) {
phy->idle_tssi = 0x34;
phy->tssi2dbm = bcm43xx_tssi2dbm_b_table;
return 0;
}
if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
pab0 != -1 && pab1 != -1 && pab2 != -1) {
/* The pabX values are set in SPROM. Use them. */
if (phy->type == BCM43xx_PHYTYPE_A) {
if ((s8)bcm->sprom.idle_tssi_tgt_aphy != 0 &&
(s8)bcm->sprom.idle_tssi_tgt_aphy != -1)
phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_aphy);
else
phy->idle_tssi = 62;
} else {
if ((s8)bcm->sprom.idle_tssi_tgt_bgphy != 0 &&
(s8)bcm->sprom.idle_tssi_tgt_bgphy != -1)
phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_bgphy);
else
phy->idle_tssi = 62;
}
dyn_tssi2dbm = kmalloc(64, GFP_KERNEL);
if (dyn_tssi2dbm == NULL) {
printk(KERN_ERR PFX "Could not allocate memory "
"for tssi2dbm table\n");
return -ENOMEM;
}
for (idx = 0; idx < 64; idx++)
if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) {
phy->tssi2dbm = NULL;
printk(KERN_ERR PFX "Could not generate "
"tssi2dBm table\n");
kfree(dyn_tssi2dbm);
return -ENODEV;
}
phy->tssi2dbm = dyn_tssi2dbm;
phy->dyn_tssi_tbl = 1;
} else {
/* pabX values not set in SPROM. */
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
/* APHY needs a generated table. */
phy->tssi2dbm = NULL;
printk(KERN_ERR PFX "Could not generate tssi2dBm "
"table (wrong SPROM info)!\n");
return -ENODEV;
case BCM43xx_PHYTYPE_B:
phy->idle_tssi = 0x34;
phy->tssi2dbm = bcm43xx_tssi2dbm_b_table;
break;
case BCM43xx_PHYTYPE_G:
phy->idle_tssi = 0x34;
phy->tssi2dbm = bcm43xx_tssi2dbm_g_table;
break;
}
}
return 0;
}
int bcm43xx_phy_init(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
int err = -ENODEV;
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
if (phy->rev == 2 || phy->rev == 3) {
bcm43xx_phy_inita(bcm);
err = 0;
}
break;
case BCM43xx_PHYTYPE_B:
switch (phy->rev) {
case 2:
bcm43xx_phy_initb2(bcm);
err = 0;
break;
case 4:
bcm43xx_phy_initb4(bcm);
err = 0;
break;
case 5:
bcm43xx_phy_initb5(bcm);
err = 0;
break;
case 6:
bcm43xx_phy_initb6(bcm);
err = 0;
break;
}
break;
case BCM43xx_PHYTYPE_G:
bcm43xx_phy_initg(bcm);
err = 0;
break;
}
if (err)
printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n");
return err;
}
void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 antennadiv;
u16 offset;
u16 value;
u32 ucodeflags;
antennadiv = phy->antenna_diversity;
if (antennadiv == 0xFFFF)
antennadiv = 3;
assert(antennadiv <= 3);
ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET);
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
ucodeflags & ~BCM43xx_UCODEFLAG_AUTODIV);
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
case BCM43xx_PHYTYPE_G:
if (phy->type == BCM43xx_PHYTYPE_A)
offset = 0x0000;
else
offset = 0x0400;
if (antennadiv == 2)
value = (3/*automatic*/ << 7);
else
value = (antennadiv << 7);
bcm43xx_phy_write(bcm, offset + 1,
(bcm43xx_phy_read(bcm, offset + 1)
& 0x7E7F) | value);
if (antennadiv >= 2) {
if (antennadiv == 2)
value = (antennadiv << 7);
else
value = (0/*force0*/ << 7);
bcm43xx_phy_write(bcm, offset + 0x2B,
(bcm43xx_phy_read(bcm, offset + 0x2B)
& 0xFEFF) | value);
}
if (phy->type == BCM43xx_PHYTYPE_G) {
if (antennadiv >= 2)
bcm43xx_phy_write(bcm, 0x048C,
bcm43xx_phy_read(bcm, 0x048C)
| 0x2000);
else
bcm43xx_phy_write(bcm, 0x048C,
bcm43xx_phy_read(bcm, 0x048C)
& ~0x2000);
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0461,
bcm43xx_phy_read(bcm, 0x0461)
| 0x0010);
bcm43xx_phy_write(bcm, 0x04AD,
(bcm43xx_phy_read(bcm, 0x04AD)
& 0x00FF) | 0x0015);
if (phy->rev == 2)
bcm43xx_phy_write(bcm, 0x0427, 0x0008);
else
bcm43xx_phy_write(bcm, 0x0427,
(bcm43xx_phy_read(bcm, 0x0427)
& 0x00FF) | 0x0008);
}
else if (phy->rev >= 6)
bcm43xx_phy_write(bcm, 0x049B, 0x00DC);
} else {
if (phy->rev < 3)
bcm43xx_phy_write(bcm, 0x002B,
(bcm43xx_phy_read(bcm, 0x002B)
& 0x00FF) | 0x0024);
else {
bcm43xx_phy_write(bcm, 0x0061,
bcm43xx_phy_read(bcm, 0x0061)
| 0x0010);
if (phy->rev == 3) {
bcm43xx_phy_write(bcm, 0x0093, 0x001D);
bcm43xx_phy_write(bcm, 0x0027, 0x0008);
} else {
bcm43xx_phy_write(bcm, 0x0093, 0x003A);
bcm43xx_phy_write(bcm, 0x0027,
(bcm43xx_phy_read(bcm, 0x0027)
& 0x00FF) | 0x0008);
}
}
}
break;
case BCM43xx_PHYTYPE_B:
if (bcm->current_core->rev == 2)
value = (3/*automatic*/ << 7);
else
value = (antennadiv << 7);
bcm43xx_phy_write(bcm, 0x03E2,
(bcm43xx_phy_read(bcm, 0x03E2)
& 0xFE7F) | value);
break;
default:
assert(0);
}
if (antennadiv >= 2) {
ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET);
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
ucodeflags | BCM43xx_UCODEFLAG_AUTODIV);
}
phy->antenna_diversity = antennadiv;
}
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef BCM43xx_PHY_H_
#define BCM43xx_PHY_H_
#include <linux/types.h>
struct bcm43xx_private;
void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm);
#define bcm43xx_phy_lock(bcm, flags) \
do { \
local_irq_save(flags); \
bcm43xx_raw_phy_lock(bcm); \
} while (0)
void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm);
#define bcm43xx_phy_unlock(bcm, flags) \
do { \
bcm43xx_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)->connected))
u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset);
void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val);
int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm);
int bcm43xx_phy_init(struct bcm43xx_private *bcm);
void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm);
void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm);
int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect);
void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm);
void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm);
void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm);
/* Adjust the LocalOscillator to the saved values.
* "fixed" is only set to 1 once in initialization. Set to 0 otherwise.
*/
void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed);
void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm);
void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm,
u16 baseband_attenuation);
#endif /* BCM43xx_PHY_H_ */
/*
Broadcom BCM43xx wireless driver
PIO Transmission
Copyright (c) 2005 Michael Buesch <mbuesch@freenet.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 "bcm43xx.h"
#include "bcm43xx_pio.h"
#include "bcm43xx_main.h"
#include "bcm43xx_xmit.h"
#include "bcm43xx_power.h"
#include <linux/delay.h>
static void tx_start(struct bcm43xx_pioqueue *queue)
{
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
BCM43xx_PIO_TXCTL_INIT);
}
static void tx_octet(struct bcm43xx_pioqueue *queue,
u8 octet)
{
if (queue->need_workarounds) {
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
octet);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
BCM43xx_PIO_TXCTL_WRITELO);
} else {
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
BCM43xx_PIO_TXCTL_WRITELO);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
octet);
}
}
static u16 tx_get_next_word(struct bcm43xx_txhdr *txhdr,
const u8 *packet,
unsigned int *pos)
{
const u8 *source;
unsigned int i = *pos;
u16 ret;
if (i < sizeof(*txhdr)) {
source = (const u8 *)txhdr;
} else {
source = packet;
i -= sizeof(*txhdr);
}
ret = le16_to_cpu( *((__le16 *)(source + i)) );
*pos += 2;
return ret;
}
static void tx_data(struct bcm43xx_pioqueue *queue,
struct bcm43xx_txhdr *txhdr,
const u8 *packet,
unsigned int octets)
{
u16 data;
unsigned int i = 0;
if (queue->need_workarounds) {
data = tx_get_next_word(txhdr, packet, &i);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data);
}
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
BCM43xx_PIO_TXCTL_WRITELO |
BCM43xx_PIO_TXCTL_WRITEHI);
while (i < octets - 1) {
data = tx_get_next_word(txhdr, packet, &i);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data);
}
if (octets % 2)
tx_octet(queue, packet[octets - sizeof(*txhdr) - 1]);
}
static void tx_complete(struct bcm43xx_pioqueue *queue,
struct sk_buff *skb)
{
if (queue->need_workarounds) {
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
skb->data[skb->len - 1]);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
BCM43xx_PIO_TXCTL_WRITELO |
BCM43xx_PIO_TXCTL_COMPLETE);
} else {
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
BCM43xx_PIO_TXCTL_COMPLETE);
}
}
static u16 generate_cookie(struct bcm43xx_pioqueue *queue,
struct bcm43xx_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 BCM43xx_MMIO_PIO1_BASE:
break;
case BCM43xx_MMIO_PIO2_BASE:
cookie = 0x1000;
break;
case BCM43xx_MMIO_PIO3_BASE:
cookie = 0x2000;
break;
case BCM43xx_MMIO_PIO4_BASE:
cookie = 0x3000;
break;
default:
assert(0);
}
packetindex = pio_txpacket_getindex(packet);
assert(((u16)packetindex & 0xF000) == 0x0000);
cookie |= (u16)packetindex;
return cookie;
}
static
struct bcm43xx_pioqueue * parse_cookie(struct bcm43xx_private *bcm,
u16 cookie,
struct bcm43xx_pio_txpacket **packet)
{
struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm);
struct bcm43xx_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:
assert(0);
}
packetindex = (cookie & 0x0FFF);
assert(packetindex >= 0 && packetindex < BCM43xx_PIO_MAXTXPACKETS);
*packet = &(queue->tx_packets_cache[packetindex]);
return queue;
}
static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue,
struct sk_buff *skb,
struct bcm43xx_pio_txpacket *packet)
{
struct bcm43xx_txhdr txhdr;
unsigned int octets;
assert(skb_shinfo(skb)->nr_frags == 0);
bcm43xx_generate_txhdr(queue->bcm,
&txhdr, skb->data, skb->len,
(packet->xmitted_frags == 0),
generate_cookie(queue, packet));
tx_start(queue);
octets = skb->len + sizeof(txhdr);
if (queue->need_workarounds)
octets--;
tx_data(queue, &txhdr, (u8 *)skb->data, octets);
tx_complete(queue, skb);
}
static void free_txpacket(struct bcm43xx_pio_txpacket *packet,
int irq_context)
{
struct bcm43xx_pioqueue *queue = packet->queue;
ieee80211_txb_free(packet->txb);
list_move(&packet->list, &queue->txfree);
queue->nr_txfree++;
assert(queue->tx_devq_used >= packet->xmitted_octets);
assert(queue->tx_devq_packets >= packet->xmitted_frags);
queue->tx_devq_used -= packet->xmitted_octets;
queue->tx_devq_packets -= packet->xmitted_frags;
}
static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet)
{
struct bcm43xx_pioqueue *queue = packet->queue;
struct ieee80211_txb *txb = packet->txb;
struct sk_buff *skb;
u16 octets;
int i;
for (i = packet->xmitted_frags; i < txb->nr_frags; i++) {
skb = txb->fragments[i];
octets = (u16)skb->len + sizeof(struct bcm43xx_txhdr);
assert(queue->tx_devq_size >= octets);
assert(queue->tx_devq_packets <= BCM43xx_PIO_MAXTXDEVQPACKETS);
assert(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 == BCM43xx_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);
/* Account for the packet size.
* (We must not overflow the device TX queue)
*/
queue->tx_devq_packets++;
queue->tx_devq_used += octets;
assert(packet->xmitted_frags < packet->txb->nr_frags);
packet->xmitted_frags++;
packet->xmitted_octets += octets;
}
list_move_tail(&packet->list, &queue->txrunning);
return 0;
}
static void tx_tasklet(unsigned long d)
{
struct bcm43xx_pioqueue *queue = (struct bcm43xx_pioqueue *)d;
struct bcm43xx_private *bcm = queue->bcm;
unsigned long flags;
struct bcm43xx_pio_txpacket *packet, *tmp_packet;
int err;
u16 txctl;
spin_lock_irqsave(&bcm->irq_lock, flags);
if (queue->tx_frozen)
goto out_unlock;
txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL);
if (txctl & BCM43xx_PIO_TXCTL_SUSPEND)
goto out_unlock;
list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) {
assert(packet->xmitted_frags < packet->txb->nr_frags);
if (packet->xmitted_frags == 0) {
int i;
struct sk_buff *skb;
/* Check if the device queue is big
* enough for every fragment. If not, drop the
* whole packet.
*/
for (i = 0; i < packet->txb->nr_frags; i++) {
skb = packet->txb->fragments[i];
if (unlikely(skb->len > queue->tx_devq_size)) {
dprintkl(KERN_ERR PFX "PIO TX device queue too small. "
"Dropping packet.\n");
free_txpacket(packet, 1);
goto next_packet;
}
}
}
/* Try to transmit the packet.
* This may not completely succeed.
*/
err = pio_tx_packet(packet);
if (err)
break;
next_packet:
continue;
}
out_unlock:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
}
static void setup_txqueues(struct bcm43xx_pioqueue *queue)
{
struct bcm43xx_pio_txpacket *packet;
int i;
queue->nr_txfree = BCM43xx_PIO_MAXTXPACKETS;
for (i = 0; i < BCM43xx_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 bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_private *bcm,
u16 pio_mmio_base)
{
struct bcm43xx_pioqueue *queue;
u32 value;
u16 qsize;
queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue)
goto out;
queue->bcm = bcm;
queue->mmio_base = pio_mmio_base;
queue->need_workarounds = (bcm->current_core->rev < 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 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
value &= ~BCM43xx_SBF_XFER_REG_BYTESWAP;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value);
qsize = bcm43xx_read16(bcm, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE);
if (qsize == 0) {
printk(KERN_ERR PFX "ERROR: This card does not support PIO "
"operation mode. Please use DMA mode "
"(module parameter pio=0).\n");
goto err_freequeue;
}
if (qsize <= BCM43xx_PIO_TXQADJUST) {
printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n",
qsize);
goto err_freequeue;
}
qsize -= BCM43xx_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 bcm43xx_pioqueue *queue)
{
struct bcm43xx_pio_txpacket *packet, *tmp_packet;
netif_tx_disable(queue->bcm->net_dev);
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 bcm43xx_destroy_pioqueue(struct bcm43xx_pioqueue *queue)
{
if (!queue)
return;
cancel_transfers(queue);
kfree(queue);
}
void bcm43xx_pio_free(struct bcm43xx_private *bcm)
{
struct bcm43xx_pio *pio;
if (!bcm43xx_using_pio(bcm))
return;
pio = bcm43xx_current_pio(bcm);
bcm43xx_destroy_pioqueue(pio->queue3);
pio->queue3 = NULL;
bcm43xx_destroy_pioqueue(pio->queue2);
pio->queue2 = NULL;
bcm43xx_destroy_pioqueue(pio->queue1);
pio->queue1 = NULL;
bcm43xx_destroy_pioqueue(pio->queue0);
pio->queue0 = NULL;
}
int bcm43xx_pio_init(struct bcm43xx_private *bcm)
{
struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm);
struct bcm43xx_pioqueue *queue;
int err = -ENOMEM;
queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO1_BASE);
if (!queue)
goto out;
pio->queue0 = queue;
queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO2_BASE);
if (!queue)
goto err_destroy0;
pio->queue1 = queue;
queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO3_BASE);
if (!queue)
goto err_destroy1;
pio->queue2 = queue;
queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO4_BASE);
if (!queue)
goto err_destroy2;
pio->queue3 = queue;
if (bcm->current_core->rev < 3)
bcm->irq_savedstate |= BCM43xx_IRQ_PIO_WORKAROUND;
dprintk(KERN_INFO PFX "PIO initialized\n");
err = 0;
out:
return err;
err_destroy2:
bcm43xx_destroy_pioqueue(pio->queue2);
pio->queue2 = NULL;
err_destroy1:
bcm43xx_destroy_pioqueue(pio->queue1);
pio->queue1 = NULL;
err_destroy0:
bcm43xx_destroy_pioqueue(pio->queue0);
pio->queue0 = NULL;
goto out;
}
int bcm43xx_pio_tx(struct bcm43xx_private *bcm,
struct ieee80211_txb *txb)
{
struct bcm43xx_pioqueue *queue = bcm43xx_current_pio(bcm)->queue1;
struct bcm43xx_pio_txpacket *packet;
assert(!queue->tx_suspended);
assert(!list_empty(&queue->txfree));
packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list);
packet->txb = txb;
packet->xmitted_frags = 0;
packet->xmitted_octets = 0;
list_move_tail(&packet->list, &queue->txqueue);
queue->nr_txfree--;
assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS);
/* Suspend TX, if we are out of packets in the "free" queue. */
if (list_empty(&queue->txfree)) {
netif_stop_queue(queue->bcm->net_dev);
queue->tx_suspended = 1;
}
tasklet_schedule(&queue->txtask);
return 0;
}
void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status)
{
struct bcm43xx_pioqueue *queue;
struct bcm43xx_pio_txpacket *packet;
queue = parse_cookie(bcm, status->cookie, &packet);
assert(queue);
free_txpacket(packet, 1);
if (queue->tx_suspended) {
queue->tx_suspended = 0;
netif_wake_queue(queue->bcm->net_dev);
}
/* If there are packets on the txqueue, poke the tasklet
* to transmit them.
*/
if (!list_empty(&queue->txqueue))
tasklet_schedule(&queue->txtask);
}
static void pio_rx_error(struct bcm43xx_pioqueue *queue,
int clear_buffers,
const char *error)
{
int i;
printkl("PIO RX error: %s\n", error);
bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL,
BCM43xx_PIO_RXCTL_READY);
if (clear_buffers) {
assert(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE);
for (i = 0; i < 15; i++) {
/* Dummy read. */
bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
}
}
}
void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue)
{
__le16 preamble[21] = { 0 };
struct bcm43xx_rxhdr *rxhdr;
u16 tmp, len, rxflags2;
int i, preamble_readwords;
struct sk_buff *skb;
tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL);
if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE))
return;
bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL,
BCM43xx_PIO_RXCTL_DATAAVAILABLE);
for (i = 0; i < 10; i++) {
tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL);
if (tmp & BCM43xx_PIO_RXCTL_READY)
goto data_ready;
udelay(10);
}
dprintkl(KERN_ERR PFX "PIO RX timed out\n");
return;
data_ready:
len = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
if (unlikely(len > 0x700)) {
pio_rx_error(queue, 0, "len > 0x700");
return;
}
if (unlikely(len == 0 && queue->mmio_base != BCM43xx_MMIO_PIO4_BASE)) {
pio_rx_error(queue, 0, "len == 0");
return;
}
preamble[0] = cpu_to_le16(len);
if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE)
preamble_readwords = 14 / sizeof(u16);
else
preamble_readwords = 18 / sizeof(u16);
for (i = 0; i < preamble_readwords; i++) {
tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
preamble[i + 1] = cpu_to_le16(tmp);
}
rxhdr = (struct bcm43xx_rxhdr *)preamble;
rxflags2 = le16_to_cpu(rxhdr->flags2);
if (unlikely(rxflags2 & BCM43xx_RXHDR_FLAGS2_INVALIDFRAME)) {
pio_rx_error(queue,
(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE),
"invalid frame");
return;
}
if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) {
/* We received an xmit status. */
struct bcm43xx_hwxmitstatus *hw;
struct bcm43xx_xmitstatus stat;
hw = (struct bcm43xx_hwxmitstatus *)(preamble + 1);
stat.cookie = le16_to_cpu(hw->cookie);
stat.flags = hw->flags;
stat.cnt1 = hw->cnt1;
stat.cnt2 = hw->cnt2;
stat.seq = le16_to_cpu(hw->seq);
stat.unknown = le16_to_cpu(hw->unknown);
bcm43xx_debugfs_log_txstat(queue->bcm, &stat);
bcm43xx_pio_handle_xmitstatus(queue->bcm, &stat);
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 = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
*((__le16 *)(skb->data + i)) = cpu_to_le16(tmp);
}
if (len % 2) {
tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
skb->data[len - 1] = (tmp & 0x00FF);
/* The specs say the following is required, but
* it is wrong and corrupts the PLCP. If we don't do
* this, the PLCP seems to be correct. So ifdef it out for now.
*/
#if 0
if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME)
skb->data[2] = (tmp & 0xFF00) >> 8;
else
skb->data[0] = (tmp & 0xFF00) >> 8;
#endif
}
skb_trim(skb, len - IEEE80211_FCS_LEN);
bcm43xx_rx(queue->bcm, skb, rxhdr);
}
void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue)
{
bcm43xx_power_saving_ctl_bits(queue->bcm, -1, 1);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL)
| BCM43xx_PIO_TXCTL_SUSPEND);
}
void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue)
{
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL)
& ~BCM43xx_PIO_TXCTL_SUSPEND);
bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1);
if (!list_empty(&queue->txqueue))
tasklet_schedule(&queue->txtask);
}
void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm)
{
struct bcm43xx_pio *pio;
assert(bcm43xx_using_pio(bcm));
pio = bcm43xx_current_pio(bcm);
pio->queue0->tx_frozen = 1;
pio->queue1->tx_frozen = 1;
pio->queue2->tx_frozen = 1;
pio->queue3->tx_frozen = 1;
}
void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm)
{
struct bcm43xx_pio *pio;
assert(bcm43xx_using_pio(bcm));
pio = bcm43xx_current_pio(bcm);
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 BCM43xx_PIO_H_
#define BCM43xx_PIO_H_
#include "bcm43xx.h"
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#define BCM43xx_PIO_TXCTL 0x00
#define BCM43xx_PIO_TXDATA 0x02
#define BCM43xx_PIO_TXQBUFSIZE 0x04
#define BCM43xx_PIO_RXCTL 0x08
#define BCM43xx_PIO_RXDATA 0x0A
#define BCM43xx_PIO_TXCTL_WRITELO (1 << 0)
#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 1)
#define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2)
#define BCM43xx_PIO_TXCTL_INIT (1 << 3)
#define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7)
#define BCM43xx_PIO_RXCTL_DATAAVAILABLE (1 << 0)
#define BCM43xx_PIO_RXCTL_READY (1 << 1)
/* PIO constants */
#define BCM43xx_PIO_MAXTXDEVQPACKETS 31
#define BCM43xx_PIO_TXQADJUST 80
/* PIO tuning knobs */
#define BCM43xx_PIO_MAXTXPACKETS 256
#ifdef CONFIG_BCM43XX_PIO
struct bcm43xx_pioqueue;
struct bcm43xx_xmitstatus;
struct bcm43xx_pio_txpacket {
struct bcm43xx_pioqueue *queue;
struct ieee80211_txb *txb;
struct list_head list;
u8 xmitted_frags;
u16 xmitted_octets;
};
#define pio_txpacket_getindex(packet) ((int)((packet) - (packet)->queue->tx_packets_cache))
struct bcm43xx_pioqueue {
struct bcm43xx_private *bcm;
u16 mmio_base;
u8 tx_suspended:1,
tx_frozen:1,
need_workarounds:1; /* 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 bcm43xx_pio_txpacket tx_packets_cache[BCM43xx_PIO_MAXTXPACKETS];
};
static inline
u16 bcm43xx_pio_read(struct bcm43xx_pioqueue *queue,
u16 offset)
{
return bcm43xx_read16(queue->bcm, queue->mmio_base + offset);
}
static inline
void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue,
u16 offset, u16 value)
{
bcm43xx_write16(queue->bcm, queue->mmio_base + offset, value);
mmiowb();
}
int bcm43xx_pio_init(struct bcm43xx_private *bcm);
void bcm43xx_pio_free(struct bcm43xx_private *bcm);
int bcm43xx_pio_tx(struct bcm43xx_private *bcm,
struct ieee80211_txb *txb);
void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status);
void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue);
/* Suspend a TX queue on hardware level. */
void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue);
void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue);
/* Suspend (freeze) the TX tasklet (software level). */
void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm);
void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm);
#else /* CONFIG_BCM43XX_PIO */
static inline
int bcm43xx_pio_init(struct bcm43xx_private *bcm)
{
return 0;
}
static inline
void bcm43xx_pio_free(struct bcm43xx_private *bcm)
{
}
static inline
int bcm43xx_pio_tx(struct bcm43xx_private *bcm,
struct ieee80211_txb *txb)
{
return 0;
}
static inline
void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
struct bcm43xx_xmitstatus *status)
{
}
static inline
void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue)
{
}
static inline
void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue)
{
}
static inline
void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue)
{
}
static inline
void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm)
{
}
static inline
void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm)
{
}
#endif /* CONFIG_BCM43XX_PIO */
#endif /* BCM43xx_PIO_H_ */
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <linux/delay.h>
#include "bcm43xx.h"
#include "bcm43xx_power.h"
#include "bcm43xx_main.h"
/* Get the Slow Clock Source */
static int bcm43xx_pctl_get_slowclksrc(struct bcm43xx_private *bcm)
{
u32 tmp;
int err;
assert(bcm->current_core == &bcm->core_chipcommon);
if (bcm->current_core->rev < 6) {
if (bcm->bustype == BCM43xx_BUSTYPE_PCMCIA ||
bcm->bustype == BCM43xx_BUSTYPE_SB)
return BCM43xx_PCTL_CLKSRC_XTALOS;
if (bcm->bustype == BCM43xx_BUSTYPE_PCI) {
err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp);
assert(!err);
if (tmp & 0x10)
return BCM43xx_PCTL_CLKSRC_PCI;
return BCM43xx_PCTL_CLKSRC_XTALOS;
}
}
if (bcm->current_core->rev < 10) {
tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
tmp &= 0x7;
if (tmp == 0)
return BCM43xx_PCTL_CLKSRC_LOPWROS;
if (tmp == 1)
return BCM43xx_PCTL_CLKSRC_XTALOS;
if (tmp == 2)
return BCM43xx_PCTL_CLKSRC_PCI;
}
return BCM43xx_PCTL_CLKSRC_XTALOS;
}
/* Get max/min slowclock frequency
* as described in http://bcm-specs.sipsolutions.net/PowerControl
*/
static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm,
int get_max)
{
int limit;
int clocksrc;
int divisor;
u32 tmp;
assert(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL);
assert(bcm->current_core == &bcm->core_chipcommon);
clocksrc = bcm43xx_pctl_get_slowclksrc(bcm);
if (bcm->current_core->rev < 6) {
switch (clocksrc) {
case BCM43xx_PCTL_CLKSRC_PCI:
divisor = 64;
break;
case BCM43xx_PCTL_CLKSRC_XTALOS:
divisor = 32;
break;
default:
assert(0);
divisor = 1;
}
} else if (bcm->current_core->rev < 10) {
switch (clocksrc) {
case BCM43xx_PCTL_CLKSRC_LOPWROS:
divisor = 1;
break;
case BCM43xx_PCTL_CLKSRC_XTALOS:
case BCM43xx_PCTL_CLKSRC_PCI:
tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
divisor = ((tmp & 0xFFFF0000) >> 16) + 1;
divisor *= 4;
break;
default:
assert(0);
divisor = 1;
}
} else {
tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL);
divisor = ((tmp & 0xFFFF0000) >> 16) + 1;
divisor *= 4;
}
switch (clocksrc) {
case BCM43xx_PCTL_CLKSRC_LOPWROS:
if (get_max)
limit = 43000;
else
limit = 25000;
break;
case BCM43xx_PCTL_CLKSRC_XTALOS:
if (get_max)
limit = 20200000;
else
limit = 19800000;
break;
case BCM43xx_PCTL_CLKSRC_PCI:
if (get_max)
limit = 34000000;
else
limit = 25000000;
break;
default:
assert(0);
limit = 0;
}
limit /= divisor;
return limit;
}
/* init power control
* as described in http://bcm-specs.sipsolutions.net/PowerControl
*/
int bcm43xx_pctl_init(struct bcm43xx_private *bcm)
{
int err, maxfreq;
struct bcm43xx_coreinfo *old_core;
old_core = bcm->current_core;
err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
if (err == -ENODEV)
return 0;
if (err)
goto out;
if (bcm->chip_id == 0x4321) {
if (bcm->chip_rev == 0)
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x03A4);
if (bcm->chip_rev == 1)
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x00A4);
}
if (bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) {
if (bcm->current_core->rev >= 10) {
/* Set Idle Power clock rate to 1Mhz */
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL,
(bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL)
& 0x0000FFFF) | 0x40000);
} else {
maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,
(maxfreq * 150 + 999999) / 1000000);
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,
(maxfreq * 15 + 999999) / 1000000);
}
}
err = bcm43xx_switch_core(bcm, old_core);
assert(err == 0);
out:
return err;
}
u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm)
{
u16 delay = 0;
int err;
u32 pll_on_delay;
struct bcm43xx_coreinfo *old_core;
int minfreq;
if (bcm->bustype != BCM43xx_BUSTYPE_PCI)
goto out;
if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
goto out;
old_core = bcm->current_core;
err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
if (err == -ENODEV)
goto out;
minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0);
pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY);
delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
err = bcm43xx_switch_core(bcm, old_core);
assert(err == 0);
out:
return delay;
}
/* set the powercontrol clock
* as described in http://bcm-specs.sipsolutions.net/PowerControl
*/
int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode)
{
int err;
struct bcm43xx_coreinfo *old_core;
u32 tmp;
old_core = bcm->current_core;
err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
if (err == -ENODEV)
return 0;
if (err)
goto out;
if (bcm->core_chipcommon.rev < 6) {
if (mode == BCM43xx_PCTL_CLK_FAST) {
err = bcm43xx_pctl_set_crystal(bcm, 1);
if (err)
goto out;
}
} else {
if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) &&
(bcm->core_chipcommon.rev < 10)) {
switch (mode) {
case BCM43xx_PCTL_CLK_FAST:
tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL;
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
break;
case BCM43xx_PCTL_CLK_SLOW:
tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
tmp |= BCM43xx_PCTL_FORCE_SLOW;
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
break;
case BCM43xx_PCTL_CLK_DYNAMIC:
tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
tmp &= ~BCM43xx_PCTL_FORCE_SLOW;
tmp |= BCM43xx_PCTL_FORCE_PLL;
tmp &= ~BCM43xx_PCTL_DYN_XTAL;
bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
}
}
}
err = bcm43xx_switch_core(bcm, old_core);
assert(err == 0);
out:
return err;
}
int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on)
{
int err;
u32 in, out, outenable;
err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in);
if (err)
goto err_pci;
err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out);
if (err)
goto err_pci;
err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable);
if (err)
goto err_pci;
outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
if (on) {
if (in & 0x40)
return 0;
out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
if (err)
goto err_pci;
err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
if (err)
goto err_pci;
udelay(1000);
out &= ~BCM43xx_PCTL_PLL_POWERDOWN;
err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
if (err)
goto err_pci;
udelay(5000);
} else {
if (bcm->current_core->rev < 5)
return 0;
if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW)
return 0;
/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time?
* err = bcm43xx_switch_core(bcm, bcm->active_80211_core);
* if (err)
* return err;
* if (((bcm->current_core->rev >= 3) &&
* (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) ||
* ((bcm->current_core->rev < 3) &&
* !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4))))
* return 0;
* err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
* if (err)
* return err;
*/
err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
if (err)
goto out;
out &= ~BCM43xx_PCTL_XTAL_POWERUP;
out |= BCM43xx_PCTL_PLL_POWERDOWN;
err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
if (err)
goto err_pci;
err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
if (err)
goto err_pci;
}
out:
return err;
err_pci:
printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n");
err = -EBUSY;
goto out;
}
/* Set the PowerSavingControlBits.
* Bitvalues:
* 0 => unset the bit
* 1 => set the bit
* -1 => calculate the bit
*/
void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,
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 are 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 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
if (bit25)
status |= BCM43xx_SBF_PS1;
else
status &= ~BCM43xx_SBF_PS1;
if (bit26)
status |= BCM43xx_SBF_PS2;
else
status &= ~BCM43xx_SBF_PS2;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
if (bit26 && bcm->current_core->rev >= 5) {
for (i = 0; i < 100; i++) {
if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4)
break;
udelay(10);
}
}
}
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef BCM43xx_POWER_H_
#define BCM43xx_POWER_H_
#include <linux/types.h>
/* Clock sources */
enum {
/* PCI clock */
BCM43xx_PCTL_CLKSRC_PCI,
/* Crystal slow clock oscillator */
BCM43xx_PCTL_CLKSRC_XTALOS,
/* Low power oscillator */
BCM43xx_PCTL_CLKSRC_LOPWROS,
};
struct bcm43xx_private;
int bcm43xx_pctl_init(struct bcm43xx_private *bcm);
int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode);
int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on);
u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm);
void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,
int bit25, int bit26);
#endif /* BCM43xx_POWER_H_ */
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <linux/delay.h>
#include "bcm43xx.h"
#include "bcm43xx_main.h"
#include "bcm43xx_phy.h"
#include "bcm43xx_radio.h"
#include "bcm43xx_ilt.h"
/* Table for bcm43xx_radio_calibrationvalue() */
static const u16 rcc_table[16] = {
0x0002, 0x0003, 0x0001, 0x000F,
0x0006, 0x0007, 0x0005, 0x000F,
0x000A, 0x000B, 0x0009, 0x000F,
0x000E, 0x000F, 0x000D, 0x000F,
};
/* Reverse the bits of a 4bit value.
* Example: 1101 is flipped 1011
*/
static u16 flip_4bit(u16 value)
{
u16 flipped = 0x0000;
assert((value & ~0x000F) == 0x0000);
flipped |= (value & 0x0001) << 3;
flipped |= (value & 0x0002) << 1;
flipped |= (value & 0x0004) >> 1;
flipped |= (value & 0x0008) >> 3;
return flipped;
}
/* Get the freq, as it has to be written to the device. */
static 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,
};
assert(channel >= 1 && channel <= 14);
return frequencies_bg[channel - 1];
}
/* Get the freq, as it has to be written to the device. */
static inline
u16 channel2freq_a(u8 channel)
{
assert(channel <= 200);
return (5000 + 5 * channel);
}
void bcm43xx_radio_lock(struct bcm43xx_private *bcm)
{
u32 status;
status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
status |= BCM43xx_SBF_RADIOREG_LOCK;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
mmiowb();
udelay(10);
}
void bcm43xx_radio_unlock(struct bcm43xx_private *bcm)
{
u32 status;
bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); /* dummy read */
status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
status &= ~BCM43xx_SBF_RADIOREG_LOCK;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
mmiowb();
}
u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
offset |= 0x0040;
break;
case BCM43xx_PHYTYPE_B:
if (radio->version == 0x2053) {
if (offset < 0x70)
offset += 0x80;
else if (offset < 0x80)
offset += 0x70;
} else if (radio->version == 0x2050) {
offset |= 0x80;
} else
assert(0);
break;
case BCM43xx_PHYTYPE_G:
offset |= 0x80;
break;
}
bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset);
return bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW);
}
void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val)
{
bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset);
mmiowb();
bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW, val);
}
static void bcm43xx_set_all_gains(struct bcm43xx_private *bcm,
s16 first, s16 second, s16 third)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 i;
u16 start = 0x08, end = 0x18;
u16 offset = 0x0400;
u16 tmp;
if (phy->rev <= 1) {
offset = 0x5000;
start = 0x10;
end = 0x20;
}
for (i = 0; i < 4; i++)
bcm43xx_ilt_write(bcm, offset + i, first);
for (i = start; i < end; i++)
bcm43xx_ilt_write(bcm, offset + i, second);
if (third != -1) {
tmp = ((u16)third << 14) | ((u16)third << 6);
bcm43xx_phy_write(bcm, 0x04A0,
(bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | tmp);
bcm43xx_phy_write(bcm, 0x04A1,
(bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | tmp);
bcm43xx_phy_write(bcm, 0x04A2,
(bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | tmp);
}
bcm43xx_dummy_transmission(bcm);
}
static void bcm43xx_set_original_gains(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 i, tmp;
u16 offset = 0x0400;
u16 start = 0x0008, end = 0x0018;
if (phy->rev <= 1) {
offset = 0x5000;
start = 0x0010;
end = 0x0020;
}
for (i = 0; i < 4; i++) {
tmp = (i & 0xFFFC);
tmp |= (i & 0x0001) << 1;
tmp |= (i & 0x0002) >> 1;
bcm43xx_ilt_write(bcm, offset + i, tmp);
}
for (i = start; i < end; i++)
bcm43xx_ilt_write(bcm, offset + i, i - start);
bcm43xx_phy_write(bcm, 0x04A0,
(bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | 0x4040);
bcm43xx_phy_write(bcm, 0x04A1,
(bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | 0x4040);
bcm43xx_phy_write(bcm, 0x04A2,
(bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | 0x4000);
bcm43xx_dummy_transmission(bcm);
}
/* Synthetic PU workaround */
static void bcm43xx_synth_pu_workaround(struct bcm43xx_private *bcm, u8 channel)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
if (radio->version != 0x2050 || radio->revision >= 6) {
/* We do not need the workaround. */
return;
}
if (channel <= 10) {
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
channel2freq_bg(channel + 4));
} else {
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
channel2freq_bg(1));
}
udelay(100);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
channel2freq_bg(channel));
}
u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u8 ret = 0;
u16 saved, rssi, temp;
int i, j = 0;
saved = bcm43xx_phy_read(bcm, 0x0403);
bcm43xx_radio_selectchannel(bcm, channel, 0);
bcm43xx_phy_write(bcm, 0x0403, (saved & 0xFFF8) | 5);
if (radio->aci_hw_rssi)
rssi = bcm43xx_phy_read(bcm, 0x048A) & 0x3F;
else
rssi = saved & 0x3F;
/* clamp temp to signed 5bit */
if (rssi > 32)
rssi -= 64;
for (i = 0;i < 100; i++) {
temp = (bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x3F;
if (temp > 32)
temp -= 64;
if (temp < rssi)
j++;
if (j >= 20)
ret = 1;
}
bcm43xx_phy_write(bcm, 0x0403, saved);
return ret;
}
u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u8 ret[13];
unsigned int channel = radio->channel;
unsigned int i, j, start, end;
unsigned long phylock_flags;
if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0)))
return 0;
bcm43xx_phy_lock(bcm, phylock_flags);
bcm43xx_radio_lock(bcm);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF);
bcm43xx_set_all_gains(bcm, 3, 8, 1);
start = (channel - 5 > 0) ? channel - 5 : 1;
end = (channel + 5 < 14) ? channel + 5 : 13;
for (i = start; i <= end; i++) {
if (abs(channel - i) > 2)
ret[i-1] = bcm43xx_radio_aci_detect(bcm, i);
}
bcm43xx_radio_selectchannel(bcm, channel, 0);
bcm43xx_phy_write(bcm, 0x0802,
(bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC) | 0x0003);
bcm43xx_phy_write(bcm, 0x0403,
bcm43xx_phy_read(bcm, 0x0403) & 0xFFF8);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000);
bcm43xx_set_original_gains(bcm);
for (i = 0; i < 13; i++) {
if (!ret[i])
continue;
end = (i + 5 < 13) ? i + 5 : 13;
for (j = i; j < end; j++)
ret[j] = 1;
}
bcm43xx_radio_unlock(bcm);
bcm43xx_phy_unlock(bcm, phylock_flags);
return ret[channel - 1];
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val)
{
bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset);
mmiowb();
bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_DATA, (u16)val);
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset)
{
u16 val;
bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset);
val = bcm43xx_phy_read(bcm, BCM43xx_PHY_NRSSILT_DATA);
return (s16)val;
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val)
{
u16 i;
s16 tmp;
for (i = 0; i < 64; i++) {
tmp = bcm43xx_nrssi_hw_read(bcm, i);
tmp -= val;
tmp = limit_value(tmp, -32, 31);
bcm43xx_nrssi_hw_write(bcm, i, tmp);
}
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
s16 i, delta;
s32 tmp;
delta = 0x1F - radio->nrssi[0];
for (i = 0; i < 64; i++) {
tmp = (i - delta) * radio->nrssislope;
tmp /= 0x10000;
tmp += 0x3A;
tmp = limit_value(tmp, 0, 0x3F);
radio->nrssi_lt[i] = tmp;
}
}
static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
u16 backup[20] = { 0 };
s16 v47F;
u16 i;
u16 saved = 0xFFFF;
backup[0] = bcm43xx_phy_read(bcm, 0x0001);
backup[1] = bcm43xx_phy_read(bcm, 0x0811);
backup[2] = bcm43xx_phy_read(bcm, 0x0812);
backup[3] = bcm43xx_phy_read(bcm, 0x0814);
backup[4] = bcm43xx_phy_read(bcm, 0x0815);
backup[5] = bcm43xx_phy_read(bcm, 0x005A);
backup[6] = bcm43xx_phy_read(bcm, 0x0059);
backup[7] = bcm43xx_phy_read(bcm, 0x0058);
backup[8] = bcm43xx_phy_read(bcm, 0x000A);
backup[9] = bcm43xx_phy_read(bcm, 0x0003);
backup[10] = bcm43xx_radio_read16(bcm, 0x007A);
backup[11] = bcm43xx_radio_read16(bcm, 0x0043);
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) & 0x7FFF);
bcm43xx_phy_write(bcm, 0x0001,
(bcm43xx_phy_read(bcm, 0x0001) & 0x3FFF) | 0x4000);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812) & 0xFFF3) | 0x0004);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) & ~(0x1 | 0x2));
if (phy->rev >= 6) {
backup[12] = bcm43xx_phy_read(bcm, 0x002E);
backup[13] = bcm43xx_phy_read(bcm, 0x002F);
backup[14] = bcm43xx_phy_read(bcm, 0x080F);
backup[15] = bcm43xx_phy_read(bcm, 0x0810);
backup[16] = bcm43xx_phy_read(bcm, 0x0801);
backup[17] = bcm43xx_phy_read(bcm, 0x0060);
backup[18] = bcm43xx_phy_read(bcm, 0x0014);
backup[19] = bcm43xx_phy_read(bcm, 0x0478);
bcm43xx_phy_write(bcm, 0x002E, 0);
bcm43xx_phy_write(bcm, 0x002F, 0);
bcm43xx_phy_write(bcm, 0x080F, 0);
bcm43xx_phy_write(bcm, 0x0810, 0);
bcm43xx_phy_write(bcm, 0x0478,
bcm43xx_phy_read(bcm, 0x0478) | 0x0100);
bcm43xx_phy_write(bcm, 0x0801,
bcm43xx_phy_read(bcm, 0x0801) | 0x0040);
bcm43xx_phy_write(bcm, 0x0060,
bcm43xx_phy_read(bcm, 0x0060) | 0x0040);
bcm43xx_phy_write(bcm, 0x0014,
bcm43xx_phy_read(bcm, 0x0014) | 0x0200);
}
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0070);
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);
udelay(30);
v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F == 31) {
for (i = 7; i >= 4; i--) {
bcm43xx_radio_write16(bcm, 0x007B, i);
udelay(20);
v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F < 31 && saved == 0xFFFF)
saved = i;
}
if (saved == 0xFFFF)
saved = 4;
} else {
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0001);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) | 0x000C);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) | 0x0030);
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) | 0x0030);
bcm43xx_phy_write(bcm, 0x005A, 0x0480);
bcm43xx_phy_write(bcm, 0x0059, 0x0810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
if (phy->analog == 0) {
bcm43xx_phy_write(bcm, 0x0003, 0x0122);
} else {
bcm43xx_phy_write(bcm, 0x000A,
bcm43xx_phy_read(bcm, 0x000A)
| 0x2000);
}
bcm43xx_phy_write(bcm, 0x0814,
bcm43xx_phy_read(bcm, 0x0814) | 0x0004);
bcm43xx_phy_write(bcm, 0x0815,
bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB);
bcm43xx_phy_write(bcm, 0x0003,
(bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F)
| 0x0040);
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x000F);
bcm43xx_set_all_gains(bcm, 3, 0, 1);
bcm43xx_radio_write16(bcm, 0x0043,
(bcm43xx_radio_read16(bcm, 0x0043)
& 0x00F0) | 0x000F);
udelay(30);
v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F == -32) {
for (i = 0; i < 4; i++) {
bcm43xx_radio_write16(bcm, 0x007B, i);
udelay(20);
v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
if (v47F >= 0x20)
v47F -= 0x40;
if (v47F > -31 && saved == 0xFFFF)
saved = i;
}
if (saved == 0xFFFF)
saved = 3;
} else
saved = 0;
}
bcm43xx_radio_write16(bcm, 0x007B, saved);
if (phy->rev >= 6) {
bcm43xx_phy_write(bcm, 0x002E, backup[12]);
bcm43xx_phy_write(bcm, 0x002F, backup[13]);
bcm43xx_phy_write(bcm, 0x080F, backup[14]);
bcm43xx_phy_write(bcm, 0x0810, backup[15]);
}
bcm43xx_phy_write(bcm, 0x0814, backup[3]);
bcm43xx_phy_write(bcm, 0x0815, backup[4]);
bcm43xx_phy_write(bcm, 0x005A, backup[5]);
bcm43xx_phy_write(bcm, 0x0059, backup[6]);
bcm43xx_phy_write(bcm, 0x0058, backup[7]);
bcm43xx_phy_write(bcm, 0x000A, backup[8]);
bcm43xx_phy_write(bcm, 0x0003, backup[9]);
bcm43xx_radio_write16(bcm, 0x0043, backup[11]);
bcm43xx_radio_write16(bcm, 0x007A, backup[10]);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) | 0x1 | 0x2);
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) | 0x8000);
bcm43xx_set_original_gains(bcm);
if (phy->rev >= 6) {
bcm43xx_phy_write(bcm, 0x0801, backup[16]);
bcm43xx_phy_write(bcm, 0x0060, backup[17]);
bcm43xx_phy_write(bcm, 0x0014, backup[18]);
bcm43xx_phy_write(bcm, 0x0478, backup[19]);
}
bcm43xx_phy_write(bcm, 0x0001, backup[0]);
bcm43xx_phy_write(bcm, 0x0812, backup[2]);
bcm43xx_phy_write(bcm, 0x0811, backup[1]);
}
void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 backup[18] = { 0 };
u16 tmp;
s16 nrssi0, nrssi1;
switch (phy->type) {
case BCM43xx_PHYTYPE_B:
backup[0] = bcm43xx_radio_read16(bcm, 0x007A);
backup[1] = bcm43xx_radio_read16(bcm, 0x0052);
backup[2] = bcm43xx_radio_read16(bcm, 0x0043);
backup[3] = bcm43xx_phy_read(bcm, 0x0030);
backup[4] = bcm43xx_phy_read(bcm, 0x0026);
backup[5] = bcm43xx_phy_read(bcm, 0x0015);
backup[6] = bcm43xx_phy_read(bcm, 0x002A);
backup[7] = bcm43xx_phy_read(bcm, 0x0020);
backup[8] = bcm43xx_phy_read(bcm, 0x005A);
backup[9] = bcm43xx_phy_read(bcm, 0x0059);
backup[10] = bcm43xx_phy_read(bcm, 0x0058);
backup[11] = bcm43xx_read16(bcm, 0x03E2);
backup[12] = bcm43xx_read16(bcm, 0x03E6);
backup[13] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
tmp = bcm43xx_radio_read16(bcm, 0x007A);
tmp &= (phy->rev >= 5) ? 0x007F : 0x000F;
bcm43xx_radio_write16(bcm, 0x007A, tmp);
bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
bcm43xx_write16(bcm, 0x03EC, 0x7F7F);
bcm43xx_phy_write(bcm, 0x0026, 0x0000);
bcm43xx_phy_write(bcm, 0x0015,
bcm43xx_phy_read(bcm, 0x0015) | 0x0020);
bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);
nrssi0 = (s16)bcm43xx_phy_read(bcm, 0x0027);
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);
if (phy->analog >= 2) {
bcm43xx_write16(bcm, 0x03E6, 0x0040);
} else if (phy->analog == 0) {
bcm43xx_write16(bcm, 0x03E6, 0x0122);
} else {
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) & 0x2000);
}
bcm43xx_phy_write(bcm, 0x0020, 0x3F3F);
bcm43xx_phy_write(bcm, 0x0015, 0xF330);
bcm43xx_radio_write16(bcm, 0x005A, 0x0060);
bcm43xx_radio_write16(bcm, 0x0043,
bcm43xx_radio_read16(bcm, 0x0043) & 0x00F0);
bcm43xx_phy_write(bcm, 0x005A, 0x0480);
bcm43xx_phy_write(bcm, 0x0059, 0x0810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
udelay(20);
nrssi1 = (s16)bcm43xx_phy_read(bcm, 0x0027);
bcm43xx_phy_write(bcm, 0x0030, backup[3]);
bcm43xx_radio_write16(bcm, 0x007A, backup[0]);
bcm43xx_write16(bcm, 0x03E2, backup[11]);
bcm43xx_phy_write(bcm, 0x0026, backup[4]);
bcm43xx_phy_write(bcm, 0x0015, backup[5]);
bcm43xx_phy_write(bcm, 0x002A, backup[6]);
bcm43xx_synth_pu_workaround(bcm, radio->channel);
if (phy->analog != 0)
bcm43xx_write16(bcm, 0x03F4, backup[13]);
bcm43xx_phy_write(bcm, 0x0020, backup[7]);
bcm43xx_phy_write(bcm, 0x005A, backup[8]);
bcm43xx_phy_write(bcm, 0x0059, backup[9]);
bcm43xx_phy_write(bcm, 0x0058, backup[10]);
bcm43xx_radio_write16(bcm, 0x0052, backup[1]);
bcm43xx_radio_write16(bcm, 0x0043, backup[2]);
if (nrssi0 == nrssi1)
radio->nrssislope = 0x00010000;
else
radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
if (nrssi0 <= -4) {
radio->nrssi[0] = nrssi0;
radio->nrssi[1] = nrssi1;
}
break;
case BCM43xx_PHYTYPE_G:
if (radio->revision >= 9)
return;
if (radio->revision == 8)
bcm43xx_calc_nrssi_offset(bcm);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC);
backup[7] = bcm43xx_read16(bcm, 0x03E2);
bcm43xx_write16(bcm, 0x03E2,
bcm43xx_read16(bcm, 0x03E2) | 0x8000);
backup[0] = bcm43xx_radio_read16(bcm, 0x007A);
backup[1] = bcm43xx_radio_read16(bcm, 0x0052);
backup[2] = bcm43xx_radio_read16(bcm, 0x0043);
backup[3] = bcm43xx_phy_read(bcm, 0x0015);
backup[4] = bcm43xx_phy_read(bcm, 0x005A);
backup[5] = bcm43xx_phy_read(bcm, 0x0059);
backup[6] = bcm43xx_phy_read(bcm, 0x0058);
backup[8] = bcm43xx_read16(bcm, 0x03E6);
backup[9] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
if (phy->rev >= 3) {
backup[10] = bcm43xx_phy_read(bcm, 0x002E);
backup[11] = bcm43xx_phy_read(bcm, 0x002F);
backup[12] = bcm43xx_phy_read(bcm, 0x080F);
backup[13] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_LO_CONTROL);
backup[14] = bcm43xx_phy_read(bcm, 0x0801);
backup[15] = bcm43xx_phy_read(bcm, 0x0060);
backup[16] = bcm43xx_phy_read(bcm, 0x0014);
backup[17] = bcm43xx_phy_read(bcm, 0x0478);
bcm43xx_phy_write(bcm, 0x002E, 0);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, 0);
switch (phy->rev) {
case 4: case 6: case 7:
bcm43xx_phy_write(bcm, 0x0478,
bcm43xx_phy_read(bcm, 0x0478)
| 0x0100);
bcm43xx_phy_write(bcm, 0x0801,
bcm43xx_phy_read(bcm, 0x0801)
| 0x0040);
break;
case 3: case 5:
bcm43xx_phy_write(bcm, 0x0801,
bcm43xx_phy_read(bcm, 0x0801)
& 0xFFBF);
break;
}
bcm43xx_phy_write(bcm, 0x0060,
bcm43xx_phy_read(bcm, 0x0060)
| 0x0040);
bcm43xx_phy_write(bcm, 0x0014,
bcm43xx_phy_read(bcm, 0x0014)
| 0x0200);
}
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0070);
bcm43xx_set_all_gains(bcm, 0, 8, 0);
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) & 0x00F7);
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0811,
(bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0030);
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0010);
}
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);
udelay(20);
nrssi0 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
if (nrssi0 >= 0x0020)
nrssi0 -= 0x0040;
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);
if (phy->analog >= 2) {
bcm43xx_phy_write(bcm, 0x0003,
(bcm43xx_phy_read(bcm, 0x0003)
& 0xFF9F) | 0x0040);
}
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
| 0x2000);
bcm43xx_radio_write16(bcm, 0x007A,
bcm43xx_radio_read16(bcm, 0x007A) | 0x000F);
bcm43xx_phy_write(bcm, 0x0015, 0xF330);
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0812,
(bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0020);
bcm43xx_phy_write(bcm, 0x0811,
(bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0020);
}
bcm43xx_set_all_gains(bcm, 3, 0, 1);
if (radio->revision == 8) {
bcm43xx_radio_write16(bcm, 0x0043, 0x001F);
} else {
tmp = bcm43xx_radio_read16(bcm, 0x0052) & 0xFF0F;
bcm43xx_radio_write16(bcm, 0x0052, tmp | 0x0060);
tmp = bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0;
bcm43xx_radio_write16(bcm, 0x0043, tmp | 0x0009);
}
bcm43xx_phy_write(bcm, 0x005A, 0x0480);
bcm43xx_phy_write(bcm, 0x0059, 0x0810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
udelay(20);
nrssi1 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
if (nrssi1 >= 0x0020)
nrssi1 -= 0x0040;
if (nrssi0 == nrssi1)
radio->nrssislope = 0x00010000;
else
radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
if (nrssi0 >= -4) {
radio->nrssi[0] = nrssi1;
radio->nrssi[1] = nrssi0;
}
if (phy->rev >= 3) {
bcm43xx_phy_write(bcm, 0x002E, backup[10]);
bcm43xx_phy_write(bcm, 0x002F, backup[11]);
bcm43xx_phy_write(bcm, 0x080F, backup[12]);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, backup[13]);
}
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF);
bcm43xx_phy_write(bcm, 0x0811,
bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF);
}
bcm43xx_radio_write16(bcm, 0x007A, backup[0]);
bcm43xx_radio_write16(bcm, 0x0052, backup[1]);
bcm43xx_radio_write16(bcm, 0x0043, backup[2]);
bcm43xx_write16(bcm, 0x03E2, backup[7]);
bcm43xx_write16(bcm, 0x03E6, backup[8]);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[9]);
bcm43xx_phy_write(bcm, 0x0015, backup[3]);
bcm43xx_phy_write(bcm, 0x005A, backup[4]);
bcm43xx_phy_write(bcm, 0x0059, backup[5]);
bcm43xx_phy_write(bcm, 0x0058, backup[6]);
bcm43xx_synth_pu_workaround(bcm, radio->channel);
bcm43xx_phy_write(bcm, 0x0802,
bcm43xx_phy_read(bcm, 0x0802) | (0x0001 | 0x0002));
bcm43xx_set_original_gains(bcm);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000);
if (phy->rev >= 3) {
bcm43xx_phy_write(bcm, 0x0801, backup[14]);
bcm43xx_phy_write(bcm, 0x0060, backup[15]);
bcm43xx_phy_write(bcm, 0x0014, backup[16]);
bcm43xx_phy_write(bcm, 0x0478, backup[17]);
}
bcm43xx_nrssi_mem_update(bcm);
bcm43xx_calc_nrssi_threshold(bcm);
break;
default:
assert(0);
}
}
void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
s32 threshold;
s32 a, b;
s16 tmp16;
u16 tmp_u16;
switch (phy->type) {
case BCM43xx_PHYTYPE_B: {
if (radio->version != 0x2050)
return;
if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI))
return;
if (radio->revision >= 6) {
threshold = (radio->nrssi[1] - radio->nrssi[0]) * 32;
threshold += 20 * (radio->nrssi[0] + 1);
threshold /= 40;
} else
threshold = radio->nrssi[1] - 5;
threshold = limit_value(threshold, 0, 0x3E);
bcm43xx_phy_read(bcm, 0x0020); /* dummy read */
bcm43xx_phy_write(bcm, 0x0020, (((u16)threshold) << 8) | 0x001C);
if (radio->revision >= 6) {
bcm43xx_phy_write(bcm, 0x0087, 0x0E0D);
bcm43xx_phy_write(bcm, 0x0086, 0x0C0B);
bcm43xx_phy_write(bcm, 0x0085, 0x0A09);
bcm43xx_phy_write(bcm, 0x0084, 0x0808);
bcm43xx_phy_write(bcm, 0x0083, 0x0808);
bcm43xx_phy_write(bcm, 0x0082, 0x0604);
bcm43xx_phy_write(bcm, 0x0081, 0x0302);
bcm43xx_phy_write(bcm, 0x0080, 0x0100);
}
break;
}
case BCM43xx_PHYTYPE_G:
if (!phy->connected ||
!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) {
tmp16 = bcm43xx_nrssi_hw_read(bcm, 0x20);
if (tmp16 >= 0x20)
tmp16 -= 0x40;
if (tmp16 < 3) {
bcm43xx_phy_write(bcm, 0x048A,
(bcm43xx_phy_read(bcm, 0x048A)
& 0xF000) | 0x09EB);
} else {
bcm43xx_phy_write(bcm, 0x048A,
(bcm43xx_phy_read(bcm, 0x048A)
& 0xF000) | 0x0AED);
}
} else {
if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN) {
a = 0xE;
b = 0xA;
} else if (!radio->aci_wlan_automatic && radio->aci_enable) {
a = 0x13;
b = 0x12;
} else {
a = 0xE;
b = 0x11;
}
a = a * (radio->nrssi[1] - radio->nrssi[0]);
a += (radio->nrssi[0] << 6);
if (a < 32)
a += 31;
else
a += 32;
a = a >> 6;
a = limit_value(a, -31, 31);
b = b * (radio->nrssi[1] - radio->nrssi[0]);
b += (radio->nrssi[0] << 6);
if (b < 32)
b += 31;
else
b += 32;
b = b >> 6;
b = limit_value(b, -31, 31);
tmp_u16 = bcm43xx_phy_read(bcm, 0x048A) & 0xF000;
tmp_u16 |= ((u32)b & 0x0000003F);
tmp_u16 |= (((u32)a & 0x0000003F) << 6);
bcm43xx_phy_write(bcm, 0x048A, tmp_u16);
}
break;
default:
assert(0);
}
}
/* 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]);
assert((offset & 0xE000) == 0x0000);
assert((id & 0xF8) == 0x00);
*stackptr = offset;
*stackptr |= ((u32)id) << 13;
*stackptr |= ((u32)value) << 16;
(*stackidx)++;
assert(*stackidx < BCM43xx_INTERFSTACK_SIZE);
}
static u16 _stack_restore(u32 *stackptr,
u8 id, u16 offset)
{
size_t i;
assert((offset & 0xE000) == 0x0000);
assert((id & 0xF8) == 0x00);
for (i = 0; i < BCM43xx_INTERFSTACK_SIZE; i++, stackptr++) {
if ((*stackptr & 0x00001FFF) != offset)
continue;
if (((*stackptr & 0x00007000) >> 13) != id)
continue;
return ((*stackptr & 0xFFFF0000) >> 16);
}
assert(0);
return 0;
}
#define phy_stacksave(offset) \
do { \
_stack_save(stack, &stackidx, 0x1, (offset), \
bcm43xx_phy_read(bcm, (offset))); \
} while (0)
#define phy_stackrestore(offset) \
do { \
bcm43xx_phy_write(bcm, (offset), \
_stack_restore(stack, 0x1, \
(offset))); \
} while (0)
#define radio_stacksave(offset) \
do { \
_stack_save(stack, &stackidx, 0x2, (offset), \
bcm43xx_radio_read16(bcm, (offset))); \
} while (0)
#define radio_stackrestore(offset) \
do { \
bcm43xx_radio_write16(bcm, (offset), \
_stack_restore(stack, 0x2, \
(offset))); \
} while (0)
#define ilt_stacksave(offset) \
do { \
_stack_save(stack, &stackidx, 0x3, (offset), \
bcm43xx_ilt_read(bcm, (offset))); \
} while (0)
#define ilt_stackrestore(offset) \
do { \
bcm43xx_ilt_write(bcm, (offset), \
_stack_restore(stack, 0x3, \
(offset))); \
} while (0)
static void
bcm43xx_radio_interference_mitigation_enable(struct bcm43xx_private *bcm,
int mode)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 tmp, flipped;
u32 tmp32;
size_t stackidx = 0;
u32 *stack = radio->interfstack;
switch (mode) {
case BCM43xx_RADIO_INTERFMODE_NONWLAN:
if (phy->rev != 1) {
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B) | 0x0800);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & ~0x4000);
break;
}
radio_stacksave(0x0078);
tmp = (bcm43xx_radio_read16(bcm, 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;
bcm43xx_radio_write16(bcm, 0x0078, flipped);
bcm43xx_calc_nrssi_threshold(bcm);
phy_stacksave(0x0406);
bcm43xx_phy_write(bcm, 0x0406, 0x7E28);
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B) | 0x0800);
bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | 0x1000);
phy_stacksave(0x04A0);
bcm43xx_phy_write(bcm, 0x04A0,
(bcm43xx_phy_read(bcm, 0x04A0) & 0xC0C0) | 0x0008);
phy_stacksave(0x04A1);
bcm43xx_phy_write(bcm, 0x04A1,
(bcm43xx_phy_read(bcm, 0x04A1) & 0xC0C0) | 0x0605);
phy_stacksave(0x04A2);
bcm43xx_phy_write(bcm, 0x04A2,
(bcm43xx_phy_read(bcm, 0x04A2) & 0xC0C0) | 0x0204);
phy_stacksave(0x04A8);
bcm43xx_phy_write(bcm, 0x04A8,
(bcm43xx_phy_read(bcm, 0x04A8) & 0xC0C0) | 0x0803);
phy_stacksave(0x04AB);
bcm43xx_phy_write(bcm, 0x04AB,
(bcm43xx_phy_read(bcm, 0x04AB) & 0xC0C0) | 0x0605);
phy_stacksave(0x04A7);
bcm43xx_phy_write(bcm, 0x04A7, 0x0002);
phy_stacksave(0x04A3);
bcm43xx_phy_write(bcm, 0x04A3, 0x287A);
phy_stacksave(0x04A9);
bcm43xx_phy_write(bcm, 0x04A9, 0x2027);
phy_stacksave(0x0493);
bcm43xx_phy_write(bcm, 0x0493, 0x32F5);
phy_stacksave(0x04AA);
bcm43xx_phy_write(bcm, 0x04AA, 0x2027);
phy_stacksave(0x04AC);
bcm43xx_phy_write(bcm, 0x04AC, 0x32F5);
break;
case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
if (bcm43xx_phy_read(bcm, 0x0033) & 0x0800)
break;
radio->aci_enable = 1;
phy_stacksave(BCM43xx_PHY_RADIO_BITFIELD);
phy_stacksave(BCM43xx_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);
bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD)
& ~0x1000);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
(bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS)
& 0xFFFC) | 0x0002);
bcm43xx_phy_write(bcm, 0x0033, 0x0800);
bcm43xx_phy_write(bcm, 0x04A3, 0x2027);
bcm43xx_phy_write(bcm, 0x04A9, 0x1CA8);
bcm43xx_phy_write(bcm, 0x0493, 0x287A);
bcm43xx_phy_write(bcm, 0x04AA, 0x1CA8);
bcm43xx_phy_write(bcm, 0x04AC, 0x287A);
bcm43xx_phy_write(bcm, 0x04A0,
(bcm43xx_phy_read(bcm, 0x04A0)
& 0xFFC0) | 0x001A);
bcm43xx_phy_write(bcm, 0x04A7, 0x000D);
if (phy->rev < 2) {
bcm43xx_phy_write(bcm, 0x0406, 0xFF0D);
} else if (phy->rev == 2) {
bcm43xx_phy_write(bcm, 0x04C0, 0xFFFF);
bcm43xx_phy_write(bcm, 0x04C1, 0x00A9);
} else {
bcm43xx_phy_write(bcm, 0x04C0, 0x00C1);
bcm43xx_phy_write(bcm, 0x04C1, 0x0059);
}
bcm43xx_phy_write(bcm, 0x04A1,
(bcm43xx_phy_read(bcm, 0x04A1)
& 0xC0FF) | 0x1800);
bcm43xx_phy_write(bcm, 0x04A1,
(bcm43xx_phy_read(bcm, 0x04A1)
& 0xFFC0) | 0x0015);
bcm43xx_phy_write(bcm, 0x04A8,
(bcm43xx_phy_read(bcm, 0x04A8)
& 0xCFFF) | 0x1000);
bcm43xx_phy_write(bcm, 0x04A8,
(bcm43xx_phy_read(bcm, 0x04A8)
& 0xF0FF) | 0x0A00);
bcm43xx_phy_write(bcm, 0x04AB,
(bcm43xx_phy_read(bcm, 0x04AB)
& 0xCFFF) | 0x1000);
bcm43xx_phy_write(bcm, 0x04AB,
(bcm43xx_phy_read(bcm, 0x04AB)
& 0xF0FF) | 0x0800);
bcm43xx_phy_write(bcm, 0x04AB,
(bcm43xx_phy_read(bcm, 0x04AB)
& 0xFFCF) | 0x0010);
bcm43xx_phy_write(bcm, 0x04AB,
(bcm43xx_phy_read(bcm, 0x04AB)
& 0xFFF0) | 0x0005);
bcm43xx_phy_write(bcm, 0x04A8,
(bcm43xx_phy_read(bcm, 0x04A8)
& 0xFFCF) | 0x0010);
bcm43xx_phy_write(bcm, 0x04A8,
(bcm43xx_phy_read(bcm, 0x04A8)
& 0xFFF0) | 0x0006);
bcm43xx_phy_write(bcm, 0x04A2,
(bcm43xx_phy_read(bcm, 0x04A2)
& 0xF0FF) | 0x0800);
bcm43xx_phy_write(bcm, 0x04A0,
(bcm43xx_phy_read(bcm, 0x04A0)
& 0xF0FF) | 0x0500);
bcm43xx_phy_write(bcm, 0x04A2,
(bcm43xx_phy_read(bcm, 0x04A2)
& 0xFFF0) | 0x000B);
if (phy->rev >= 3) {
bcm43xx_phy_write(bcm, 0x048A,
bcm43xx_phy_read(bcm, 0x048A)
& ~0x8000);
bcm43xx_phy_write(bcm, 0x0415,
(bcm43xx_phy_read(bcm, 0x0415)
& 0x8000) | 0x36D8);
bcm43xx_phy_write(bcm, 0x0416,
(bcm43xx_phy_read(bcm, 0x0416)
& 0x8000) | 0x36D8);
bcm43xx_phy_write(bcm, 0x0417,
(bcm43xx_phy_read(bcm, 0x0417)
& 0xFE00) | 0x016D);
} else {
bcm43xx_phy_write(bcm, 0x048A,
bcm43xx_phy_read(bcm, 0x048A)
| 0x1000);
bcm43xx_phy_write(bcm, 0x048A,
(bcm43xx_phy_read(bcm, 0x048A)
& 0x9FFF) | 0x2000);
tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET);
if (!(tmp32 & 0x800)) {
tmp32 |= 0x800;
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
tmp32);
}
}
if (phy->rev >= 2) {
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B)
| 0x0800);
}
bcm43xx_phy_write(bcm, 0x048C,
(bcm43xx_phy_read(bcm, 0x048C)
& 0xF0FF) | 0x0200);
if (phy->rev == 2) {
bcm43xx_phy_write(bcm, 0x04AE,
(bcm43xx_phy_read(bcm, 0x04AE)
& 0xFF00) | 0x007F);
bcm43xx_phy_write(bcm, 0x04AD,
(bcm43xx_phy_read(bcm, 0x04AD)
& 0x00FF) | 0x1300);
} else if (phy->rev >= 6) {
bcm43xx_ilt_write(bcm, 0x1A00 + 0x3, 0x007F);
bcm43xx_ilt_write(bcm, 0x1A00 + 0x2, 0x007F);
bcm43xx_phy_write(bcm, 0x04AD,
bcm43xx_phy_read(bcm, 0x04AD)
& 0x00FF);
}
bcm43xx_calc_nrssi_slope(bcm);
break;
default:
assert(0);
}
}
static void
bcm43xx_radio_interference_mitigation_disable(struct bcm43xx_private *bcm,
int mode)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u32 tmp32;
u32 *stack = radio->interfstack;
switch (mode) {
case BCM43xx_RADIO_INTERFMODE_NONWLAN:
if (phy->rev != 1) {
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B) & ~0x0800);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000);
break;
}
phy_stackrestore(0x0078);
bcm43xx_calc_nrssi_threshold(bcm);
phy_stackrestore(0x0406);
bcm43xx_phy_write(bcm, 0x042B,
bcm43xx_phy_read(bcm, 0x042B) & ~0x0800);
if (!bcm->bad_frames_preempt) {
bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD)
& ~(1 << 11));
}
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
bcm43xx_phy_read(bcm, BCM43xx_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 BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
if (!(bcm43xx_phy_read(bcm, 0x0033) & 0x0800))
break;
radio->aci_enable = 0;
phy_stackrestore(BCM43xx_PHY_RADIO_BITFIELD);
phy_stackrestore(BCM43xx_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 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET);
if (tmp32 & 0x800) {
tmp32 &= ~0x800;
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
tmp32);
}
bcm43xx_calc_nrssi_slope(bcm);
break;
default:
assert(0);
}
}
#undef phy_stacksave
#undef phy_stackrestore
#undef radio_stacksave
#undef radio_stackrestore
#undef ilt_stacksave
#undef ilt_stackrestore
int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm,
int mode)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
int currentmode;
if ((phy->type != BCM43xx_PHYTYPE_G) ||
(phy->rev == 0) ||
(!phy->connected))
return -ENODEV;
radio->aci_wlan_automatic = 0;
switch (mode) {
case BCM43xx_RADIO_INTERFMODE_AUTOWLAN:
radio->aci_wlan_automatic = 1;
if (radio->aci_enable)
mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
else
mode = BCM43xx_RADIO_INTERFMODE_NONE;
break;
case BCM43xx_RADIO_INTERFMODE_NONE:
case BCM43xx_RADIO_INTERFMODE_NONWLAN:
case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
break;
default:
return -EINVAL;
}
currentmode = radio->interfmode;
if (currentmode == mode)
return 0;
if (currentmode != BCM43xx_RADIO_INTERFMODE_NONE)
bcm43xx_radio_interference_mitigation_disable(bcm, currentmode);
if (mode == BCM43xx_RADIO_INTERFMODE_NONE) {
radio->aci_enable = 0;
radio->aci_hw_rssi = 0;
} else
bcm43xx_radio_interference_mitigation_enable(bcm, mode);
radio->interfmode = mode;
return 0;
}
u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm)
{
u16 reg, index, ret;
reg = bcm43xx_radio_read16(bcm, 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 bcm43xx_get_812_value(struct bcm43xx_private *bcm, u8 lpd)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 loop_or = 0;
u16 adj_loopback_gain = phy->loopback_gain[0];
u8 loop;
u16 extern_lna_control;
if (!phy->connected)
return 0;
if (!has_loopback_gain(phy)) {
if (phy->rev < 7 || !(bcm->sprom.boardflags
& BCM43xx_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:
assert(0);
}
} 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:
assert(0);
}
}
} else {
if (radio->revision == 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 && bcm->sprom.boardflags
& BCM43xx_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:
assert(0);
}
} 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:
assert(0);
}
}
}
return 0;
}
u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 backup[21] = { 0 };
u16 ret;
u16 i, j;
u32 tmp1 = 0, tmp2 = 0;
backup[0] = bcm43xx_radio_read16(bcm, 0x0043);
backup[14] = bcm43xx_radio_read16(bcm, 0x0051);
backup[15] = bcm43xx_radio_read16(bcm, 0x0052);
backup[1] = bcm43xx_phy_read(bcm, 0x0015);
backup[16] = bcm43xx_phy_read(bcm, 0x005A);
backup[17] = bcm43xx_phy_read(bcm, 0x0059);
backup[18] = bcm43xx_phy_read(bcm, 0x0058);
if (phy->type == BCM43xx_PHYTYPE_B) {
backup[2] = bcm43xx_phy_read(bcm, 0x0030);
backup[3] = bcm43xx_read16(bcm, 0x03EC);
bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
bcm43xx_write16(bcm, 0x03EC, 0x3F3F);
} else {
if (phy->connected) {
backup[4] = bcm43xx_phy_read(bcm, 0x0811);
backup[5] = bcm43xx_phy_read(bcm, 0x0812);
backup[6] = bcm43xx_phy_read(bcm, 0x0814);
backup[7] = bcm43xx_phy_read(bcm, 0x0815);
backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS);
backup[9] = bcm43xx_phy_read(bcm, 0x0802);
bcm43xx_phy_write(bcm, 0x0814,
(bcm43xx_phy_read(bcm, 0x0814)
| 0x0003));
bcm43xx_phy_write(bcm, 0x0815,
(bcm43xx_phy_read(bcm, 0x0815)
& 0xFFFC));
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
(bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS)
& 0x7FFF));
bcm43xx_phy_write(bcm, 0x0802,
(bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC));
if (phy->rev > 1) { /* loopback gain enabled */
backup[19] = bcm43xx_phy_read(bcm, 0x080F);
backup[20] = bcm43xx_phy_read(bcm, 0x0810);
if (phy->rev >= 3)
bcm43xx_phy_write(bcm, 0x080F, 0xC020);
else
bcm43xx_phy_write(bcm, 0x080F, 0x8020);
bcm43xx_phy_write(bcm, 0x0810, 0x0000);
}
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(0, 1, 1)));
if (phy->rev < 7 || !(bcm->sprom.boardflags
& BCM43xx_BFL_EXTLNA))
bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
else
bcm43xx_phy_write(bcm, 0x0811, 0x09B3);
}
}
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
(bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000));
backup[10] = bcm43xx_phy_read(bcm, 0x0035);
bcm43xx_phy_write(bcm, 0x0035,
(bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F));
backup[11] = bcm43xx_read16(bcm, 0x03E6);
backup[12] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
// Initialization
if (phy->analog == 0) {
bcm43xx_write16(bcm, 0x03E6, 0x0122);
} else {
if (phy->analog >= 2)
bcm43xx_phy_write(bcm, 0x0003,
(bcm43xx_phy_read(bcm, 0x0003)
& 0xFFBF) | 0x0040);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
(bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
| 0x2000));
}
ret = bcm43xx_radio_calibrationvalue(bcm);
if (phy->type == BCM43xx_PHYTYPE_B)
bcm43xx_radio_write16(bcm, 0x0078, 0x0026);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(0, 1, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xBFAF);
bcm43xx_phy_write(bcm, 0x002B, 0x1403);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(0, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xBFA0);
bcm43xx_radio_write16(bcm, 0x0051,
(bcm43xx_radio_read16(bcm, 0x0051) | 0x0004));
if (radio->revision == 8)
bcm43xx_radio_write16(bcm, 0x0043, 0x001F);
else {
bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
bcm43xx_radio_write16(bcm, 0x0043,
(bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0)
| 0x0009);
}
bcm43xx_phy_write(bcm, 0x0058, 0x0000);
for (i = 0; i < 16; i++) {
bcm43xx_phy_write(bcm, 0x005A, 0x0480);
bcm43xx_phy_write(bcm, 0x0059, 0xC810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(1, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
udelay(10);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(1, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xEFB0);
udelay(10);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(1, 0, 0)));
bcm43xx_phy_write(bcm, 0x0015, 0xFFF0);
udelay(20);
tmp1 += bcm43xx_phy_read(bcm, 0x002D);
bcm43xx_phy_write(bcm, 0x0058, 0x0000);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm, LPD(1, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
}
tmp1++;
tmp1 >>= 9;
udelay(10);
bcm43xx_phy_write(bcm, 0x0058, 0x0000);
for (i = 0; i < 16; i++) {
bcm43xx_radio_write16(bcm, 0x0078, (flip_4bit(i) << 1) | 0x0020);
backup[13] = bcm43xx_radio_read16(bcm, 0x0078);
udelay(10);
for (j = 0; j < 16; j++) {
bcm43xx_phy_write(bcm, 0x005A, 0x0D80);
bcm43xx_phy_write(bcm, 0x0059, 0xC810);
bcm43xx_phy_write(bcm, 0x0058, 0x000D);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm,
LPD(1, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
udelay(10);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm,
LPD(1, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xEFB0);
udelay(10);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm,
LPD(1, 0, 0)));
bcm43xx_phy_write(bcm, 0x0015, 0xFFF0);
udelay(10);
tmp2 += bcm43xx_phy_read(bcm, 0x002D);
bcm43xx_phy_write(bcm, 0x0058, 0x0000);
if (phy->connected)
bcm43xx_phy_write(bcm, 0x0812,
bcm43xx_get_812_value(bcm,
LPD(1, 0, 1)));
bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
}
tmp2++;
tmp2 >>= 8;
if (tmp1 < tmp2)
break;
}
/* Restore the registers */
bcm43xx_phy_write(bcm, 0x0015, backup[1]);
bcm43xx_radio_write16(bcm, 0x0051, backup[14]);
bcm43xx_radio_write16(bcm, 0x0052, backup[15]);
bcm43xx_radio_write16(bcm, 0x0043, backup[0]);
bcm43xx_phy_write(bcm, 0x005A, backup[16]);
bcm43xx_phy_write(bcm, 0x0059, backup[17]);
bcm43xx_phy_write(bcm, 0x0058, backup[18]);
bcm43xx_write16(bcm, 0x03E6, backup[11]);
if (phy->analog != 0)
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[12]);
bcm43xx_phy_write(bcm, 0x0035, backup[10]);
bcm43xx_radio_selectchannel(bcm, radio->channel, 1);
if (phy->type == BCM43xx_PHYTYPE_B) {
bcm43xx_phy_write(bcm, 0x0030, backup[2]);
bcm43xx_write16(bcm, 0x03EC, backup[3]);
} else {
if (phy->connected) {
bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
(bcm43xx_read16(bcm,
BCM43xx_MMIO_PHY_RADIO) & 0x7FFF));
bcm43xx_phy_write(bcm, 0x0811, backup[4]);
bcm43xx_phy_write(bcm, 0x0812, backup[5]);
bcm43xx_phy_write(bcm, 0x0814, backup[6]);
bcm43xx_phy_write(bcm, 0x0815, backup[7]);
bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]);
bcm43xx_phy_write(bcm, 0x0802, backup[9]);
if (phy->rev > 1) {
bcm43xx_phy_write(bcm, 0x080F, backup[19]);
bcm43xx_phy_write(bcm, 0x0810, backup[20]);
}
}
}
if (i >= 15)
ret = backup[13];
return ret;
}
void bcm43xx_radio_init2060(struct bcm43xx_private *bcm)
{
int err;
bcm43xx_radio_write16(bcm, 0x0004, 0x00C0);
bcm43xx_radio_write16(bcm, 0x0005, 0x0008);
bcm43xx_radio_write16(bcm, 0x0009, 0x0040);
bcm43xx_radio_write16(bcm, 0x0005, 0x00AA);
bcm43xx_radio_write16(bcm, 0x0032, 0x008F);
bcm43xx_radio_write16(bcm, 0x0006, 0x008F);
bcm43xx_radio_write16(bcm, 0x0034, 0x008F);
bcm43xx_radio_write16(bcm, 0x002C, 0x0007);
bcm43xx_radio_write16(bcm, 0x0082, 0x0080);
bcm43xx_radio_write16(bcm, 0x0080, 0x0000);
bcm43xx_radio_write16(bcm, 0x003F, 0x00DA);
bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008);
bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0010);
bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020);
bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020);
udelay(400);
bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020) | 0x0010);
udelay(400);
bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008) | 0x0008);
bcm43xx_radio_write16(bcm, 0x0085, bcm43xx_radio_read16(bcm, 0x0085) & ~0x0010);
bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008);
bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040);
bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040) | 0x0040);
bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0008) | 0x0008);
bcm43xx_phy_write(bcm, 0x0063, 0xDDC6);
bcm43xx_phy_write(bcm, 0x0069, 0x07BE);
bcm43xx_phy_write(bcm, 0x006A, 0x0000);
err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_A, 0);
assert(err == 0);
udelay(1000);
}
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 bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm)
{
static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 };
static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A };
u16 tmp = bcm43xx_radio_read16(bcm, 0x001E);
int i, j;
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
if (tmp == (data_high[i] | data_low[j])) {
bcm43xx_phy_write(bcm, 0x0069, (i - j) << 8 | 0x00C0);
return;
}
}
}
}
int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm,
u8 channel,
int synthetic_pu_workaround)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 r8, tmp;
u16 freq;
if (!ieee80211_is_valid_channel(bcm->ieee, channel))
return -EINVAL;
if ((radio->manufact == 0x17F) &&
(radio->version == 0x2060) &&
(radio->revision == 1)) {
freq = channel2freq_a(channel);
r8 = bcm43xx_radio_read16(bcm, 0x0008);
bcm43xx_write16(bcm, 0x03F0, freq);
bcm43xx_radio_write16(bcm, 0x0008, r8);
TODO();//TODO: write max channel TX power? to Radio 0x2D
tmp = bcm43xx_radio_read16(bcm, 0x002E);
tmp &= 0x0080;
TODO();//TODO: OR tmp with the Power out estimation for this channel?
bcm43xx_radio_write16(bcm, 0x002E, tmp);
if (freq >= 4920 && freq <= 5500) {
/*
* r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F;
* = (freq * 0.025862069
*/
r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */
}
bcm43xx_radio_write16(bcm, 0x0007, (r8 << 4) | r8);
bcm43xx_radio_write16(bcm, 0x0020, (r8 << 4) | r8);
bcm43xx_radio_write16(bcm, 0x0021, (r8 << 4) | r8);
bcm43xx_radio_write16(bcm, 0x0022,
(bcm43xx_radio_read16(bcm, 0x0022)
& 0x000F) | (r8 << 4));
bcm43xx_radio_write16(bcm, 0x002A, (r8 << 4));
bcm43xx_radio_write16(bcm, 0x002B, (r8 << 4));
bcm43xx_radio_write16(bcm, 0x0008,
(bcm43xx_radio_read16(bcm, 0x0008)
& 0x00F0) | (r8 << 4));
bcm43xx_radio_write16(bcm, 0x0029,
(bcm43xx_radio_read16(bcm, 0x0029)
& 0xFF0F) | 0x00B0);
bcm43xx_radio_write16(bcm, 0x0035, 0x00AA);
bcm43xx_radio_write16(bcm, 0x0036, 0x0085);
bcm43xx_radio_write16(bcm, 0x003A,
(bcm43xx_radio_read16(bcm, 0x003A)
& 0xFF20) | freq_r3A_value(freq));
bcm43xx_radio_write16(bcm, 0x003D,
bcm43xx_radio_read16(bcm, 0x003D) & 0x00FF);
bcm43xx_radio_write16(bcm, 0x0081,
(bcm43xx_radio_read16(bcm, 0x0081)
& 0xFF7F) | 0x0080);
bcm43xx_radio_write16(bcm, 0x0035,
bcm43xx_radio_read16(bcm, 0x0035) & 0xFFEF);
bcm43xx_radio_write16(bcm, 0x0035,
(bcm43xx_radio_read16(bcm, 0x0035)
& 0xFFEF) | 0x0010);
bcm43xx_radio_set_tx_iq(bcm);
TODO(); //TODO: TSSI2dbm workaround
bcm43xx_phy_xmitpower(bcm);//FIXME correct?
} else {
if (synthetic_pu_workaround)
bcm43xx_synth_pu_workaround(bcm, channel);
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
channel2freq_bg(channel));
if (channel == 14) {
if (bcm->sprom.locale == BCM43xx_LOCALE_JAPAN) {
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET)
& ~(1 << 7));
} else {
bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET,
bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODEFLAGS_OFFSET)
| (1 << 7));
}
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
| (1 << 11));
} else {
bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
& 0xF7BF);
}
}
radio->channel = channel;
//XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states
// that 2000 usecs might suffice.
udelay(8000);
return 0;
}
void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val)
{
u16 tmp;
val <<= 8;
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0022) & 0xFCFF;
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0022, tmp | val);
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x03A8) & 0xFCFF;
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x03A8, tmp | val);
tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0054) & 0xFCFF;
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0054, tmp | val);
}
/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */
static u16 bcm43xx_get_txgain_base_band(u16 txpower)
{
u16 ret;
assert(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 bcm43xx_get_txgain_freq_power_amp(u16 txpower)
{
u16 ret;
assert(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 bcm43xx_get_txgain_dac(u16 txpower)
{
u16 ret;
assert(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 bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 pamp, base, dac, ilt;
txpower = limit_value(txpower, 0, 63);
pamp = bcm43xx_get_txgain_freq_power_amp(txpower);
pamp <<= 5;
pamp &= 0x00E0;
bcm43xx_phy_write(bcm, 0x0019, pamp);
base = bcm43xx_get_txgain_base_band(txpower);
base &= 0x000F;
bcm43xx_phy_write(bcm, 0x0017, base | 0x0020);
ilt = bcm43xx_ilt_read(bcm, 0x3001);
ilt &= 0x0007;
dac = bcm43xx_get_txgain_dac(txpower);
dac <<= 3;
dac |= ilt;
bcm43xx_ilt_write(bcm, 0x3001, dac);
radio->txpwr_offset = txpower;
TODO();
//TODO: FuncPlaceholder (Adjust BB loft cancel)
}
void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm,
u16 baseband_attenuation, u16 radio_attenuation,
u16 txpower)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
if (baseband_attenuation == 0xFFFF)
baseband_attenuation = radio->baseband_atten;
if (radio_attenuation == 0xFFFF)
radio_attenuation = radio->radio_atten;
if (txpower == 0xFFFF)
txpower = radio->txctl1;
radio->baseband_atten = baseband_attenuation;
radio->radio_atten = radio_attenuation;
radio->txctl1 = txpower;
assert(/*baseband_attenuation >= 0 &&*/ baseband_attenuation <= 11);
if (radio->revision < 6)
assert(/*radio_attenuation >= 0 &&*/ radio_attenuation <= 9);
else
assert(/* radio_attenuation >= 0 &&*/ radio_attenuation <= 31);
assert(/*txpower >= 0 &&*/ txpower <= 7);
bcm43xx_phy_set_baseband_attenuation(bcm, baseband_attenuation);
bcm43xx_radio_write16(bcm, 0x0043, radio_attenuation);
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0064, radio_attenuation);
if (radio->version == 0x2050) {
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052) & ~0x0070)
| ((txpower << 4) & 0x0070));
}
//FIXME: The spec is very weird and unclear here.
if (phy->type == BCM43xx_PHYTYPE_G)
bcm43xx_phy_lo_adjust(bcm, 0);
}
u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
if (radio->version == 0x2050 && radio->revision < 6)
return 0;
return 2;
}
u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
u16 att = 0xFFFF;
if (phy->type == BCM43xx_PHYTYPE_A)
return 0x60;
switch (radio->version) {
case 0x2053:
switch (radio->revision) {
case 1:
att = 6;
break;
}
break;
case 0x2050:
switch (radio->revision) {
case 0:
att = 5;
break;
case 1:
if (phy->type == BCM43xx_PHYTYPE_G) {
if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
bcm->board_type == 0x421 &&
bcm->board_revision >= 30)
att = 3;
else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
bcm->board_type == 0x416)
att = 3;
else
att = 1;
} else {
if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
bcm->board_type == 0x421 &&
bcm->board_revision >= 30)
att = 7;
else
att = 6;
}
break;
case 2:
if (phy->type == BCM43xx_PHYTYPE_G) {
if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
bcm->board_type == 0x421 &&
bcm->board_revision >= 30)
att = 3;
else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
bcm->board_type == 0x416)
att = 5;
else if (bcm->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 (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
bcm->board_type == 0x421) {
if (bcm->board_revision < 0x43)
att = 2;
else if (bcm->board_revision < 0x51)
att = 3;
}
if (att == 0xFFFF)
att = 5;
return att;
}
u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
if (radio->version != 0x2050)
return 0;
if (radio->revision == 1)
return 3;
if (radio->revision < 6)
return 2;
if (radio->revision == 8)
return 1;
return 0;
}
void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
int err;
if (radio->enabled)
return;
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
bcm43xx_radio_write16(bcm, 0x0004, 0x00C0);
bcm43xx_radio_write16(bcm, 0x0005, 0x0008);
bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) & 0xFFF7);
bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) & 0xFFF7);
bcm43xx_radio_init2060(bcm);
break;
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G:
bcm43xx_phy_write(bcm, 0x0015, 0x8000);
bcm43xx_phy_write(bcm, 0x0015, 0xCC00);
bcm43xx_phy_write(bcm, 0x0015, (phy->connected ? 0x00C0 : 0x0000));
err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 1);
assert(err == 0);
break;
default:
assert(0);
}
radio->enabled = 1;
dprintk(KERN_INFO PFX "Radio turned on\n");
bcm43xx_leds_update(bcm, 0);
}
void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
if (phy->type == BCM43xx_PHYTYPE_A) {
bcm43xx_radio_write16(bcm, 0x0004, 0x00FF);
bcm43xx_radio_write16(bcm, 0x0005, 0x00FB);
bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) | 0x0008);
bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) | 0x0008);
}
if (phy->type == BCM43xx_PHYTYPE_G && bcm->current_core->rev >= 5) {
bcm43xx_phy_write(bcm, 0x0811, bcm43xx_phy_read(bcm, 0x0811) | 0x008C);
bcm43xx_phy_write(bcm, 0x0812, bcm43xx_phy_read(bcm, 0x0812) & 0xFF73);
} else
bcm43xx_phy_write(bcm, 0x0015, 0xAA00);
radio->enabled = 0;
dprintk(KERN_INFO PFX "Radio initialized\n");
bcm43xx_leds_update(bcm, 0);
}
void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0068, 0x7F7F);
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x006a, 0x7F7F);
break;
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G:
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0058, 0x7F7F);
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x005a, 0x7F7F);
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0070, 0x7F7F);
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0072, 0x7F7F);
break;
}
}
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef BCM43xx_RADIO_H_
#define BCM43xx_RADIO_H_
#include "bcm43xx.h"
#define BCM43xx_RADIO_DEFAULT_CHANNEL_A 36
#define BCM43xx_RADIO_DEFAULT_CHANNEL_BG 6
/* Force antenna 0. */
#define BCM43xx_RADIO_TXANTENNA_0 0
/* Force antenna 1. */
#define BCM43xx_RADIO_TXANTENNA_1 1
/* Use the RX antenna, that was selected for the most recently
* received good PLCP header.
*/
#define BCM43xx_RADIO_TXANTENNA_LASTPLCP 3
#define BCM43xx_RADIO_TXANTENNA_DEFAULT BCM43xx_RADIO_TXANTENNA_LASTPLCP
#define BCM43xx_RADIO_INTERFMODE_NONE 0
#define BCM43xx_RADIO_INTERFMODE_NONWLAN 1
#define BCM43xx_RADIO_INTERFMODE_MANUALWLAN 2
#define BCM43xx_RADIO_INTERFMODE_AUTOWLAN 3
void bcm43xx_radio_lock(struct bcm43xx_private *bcm);
void bcm43xx_radio_unlock(struct bcm43xx_private *bcm);
u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset);
void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val);
u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm);
void bcm43xx_radio_init2060(struct bcm43xx_private *bcm);
void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm);
void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm);
static inline
int bcm43xx_is_hw_radio_enabled(struct bcm43xx_private *bcm)
{
/* function to return state of hardware enable of radio
* returns 0 if radio disabled, 1 if radio enabled
*/
if (bcm->current_core->rev >= 3)
return ((bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI)
& BCM43xx_MMIO_RADIO_HWENABLED_HI_MASK)
== 0) ? 1 : 0;
else
return ((bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO)
& BCM43xx_MMIO_RADIO_HWENABLED_LO_MASK)
== 0) ? 0 : 1;
}
int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u8 channel,
int synthetic_pu_workaround);
void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower);
void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm,
u16 baseband_attenuation, u16 attenuation,
u16 txpower);
u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm);
u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm);
u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm);
void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val);
void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm);
u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel);
u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm);
int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, int mode);
void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm);
void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm);
s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset);
void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val);
void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val);
void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm);
void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm);
u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm);
#endif /* BCM43xx_RADIO_H_ */
/*
Broadcom BCM43xx wireless driver
SYSFS support routines
Copyright (c) 2006 Michael Buesch <mbuesch@freenet.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 "bcm43xx_sysfs.h"
#include "bcm43xx.h"
#include "bcm43xx_main.h"
#include "bcm43xx_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 int sprom2hex(const u16 *sprom, char *buf, size_t buf_len)
{
int i, pos = 0;
for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
pos += snprintf(buf + pos, buf_len - pos - 1,
"%04X", swab16(sprom[i]) & 0xFFFF);
}
pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
return pos + 1;
}
static int hex2sprom(u16 *sprom, const char *dump, size_t len)
{
char tmp[5] = { 0 };
int cnt = 0;
unsigned long parsed;
if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
return -EINVAL;
while (cnt < BCM43xx_SPROM_SIZE) {
memcpy(tmp, dump, 4);
dump += 4;
parsed = simple_strtoul(tmp, NULL, 16);
sprom[cnt++] = swab16((u16)parsed);
}
return 0;
}
static ssize_t bcm43xx_attr_sprom_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
u16 *sprom;
unsigned long flags;
int err;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);
sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
GFP_KERNEL);
if (!sprom)
return -ENOMEM;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
err = bcm43xx_sprom_read(bcm, sprom);
if (!err)
err = sprom2hex(sprom, buf, PAGE_SIZE);
mmiowb();
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
kfree(sprom);
return err;
}
static ssize_t bcm43xx_attr_sprom_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
u16 *sprom;
unsigned long flags;
int err;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
GFP_KERNEL);
if (!sprom)
return -ENOMEM;
err = hex2sprom(sprom, buf, count);
if (err)
goto out_kfree;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
spin_lock(&bcm->leds_lock);
err = bcm43xx_sprom_write(bcm, sprom);
mmiowb();
spin_unlock(&bcm->leds_lock);
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
out_kfree:
kfree(sprom);
return err ? err : count;
}
static DEVICE_ATTR(sprom, 0600,
bcm43xx_attr_sprom_show,
bcm43xx_attr_sprom_store);
static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
ssize_t count = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mutex_lock(&bcm->mutex);
switch (bcm43xx_current_radio(bcm)->interfmode) {
case BCM43xx_RADIO_INTERFMODE_NONE:
count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");
break;
case BCM43xx_RADIO_INTERFMODE_NONWLAN:
count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");
break;
case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");
break;
default:
assert(0);
}
mutex_unlock(&bcm->mutex);
return count;
}
static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct bcm43xx_private *bcm = dev_to_bcm(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 = BCM43xx_RADIO_INTERFMODE_NONE;
break;
case 1:
mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
break;
case 2:
mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
break;
case 3:
mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
break;
default:
return -EINVAL;
}
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
if (err) {
printk(KERN_ERR PFX "Interference Mitigation not "
"supported by device\n");
}
mmiowb();
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return err ? err : count;
}
static DEVICE_ATTR(interference, 0644,
bcm43xx_attr_interfmode_show,
bcm43xx_attr_interfmode_store);
static ssize_t bcm43xx_attr_preamble_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
ssize_t count;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mutex_lock(&bcm->mutex);
if (bcm->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(&bcm->mutex);
return count;
}
static ssize_t bcm43xx_attr_preamble_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct bcm43xx_private *bcm = dev_to_bcm(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(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
bcm->short_preamble = !!value;
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return count;
}
static DEVICE_ATTR(shortpreamble, 0644,
bcm43xx_attr_preamble_show,
bcm43xx_attr_preamble_store);
static ssize_t bcm43xx_attr_phymode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
int phytype;
int err = -EINVAL;
if (count < 1)
goto out;
switch (buf[0]) {
case 'a': case 'A':
phytype = BCM43xx_PHYTYPE_A;
break;
case 'b': case 'B':
phytype = BCM43xx_PHYTYPE_B;
break;
case 'g': case 'G':
phytype = BCM43xx_PHYTYPE_G;
break;
default:
goto out;
}
bcm43xx_cancel_work(bcm);
mutex_lock(&(bcm)->mutex);
err = bcm43xx_select_wireless_core(bcm, phytype);
if (!err)
bcm43xx_periodic_tasks_setup(bcm);
mutex_unlock(&(bcm)->mutex);
if (err == -ESRCH)
err = -ENODEV;
out:
return err ? err : count;
}
static ssize_t bcm43xx_attr_phymode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bcm43xx_private *bcm = dev_to_bcm(dev);
ssize_t count = 0;
mutex_lock(&(bcm)->mutex);
switch (bcm43xx_current_phy(bcm)->type) {
case BCM43xx_PHYTYPE_A:
snprintf(buf, PAGE_SIZE, "A");
break;
case BCM43xx_PHYTYPE_B:
snprintf(buf, PAGE_SIZE, "B");
break;
case BCM43xx_PHYTYPE_G:
snprintf(buf, PAGE_SIZE, "G");
break;
default:
assert(0);
}
mutex_unlock(&(bcm)->mutex);
return count;
}
static DEVICE_ATTR(phymode, 0644,
bcm43xx_attr_phymode_show,
bcm43xx_attr_phymode_store);
static ssize_t bcm43xx_attr_microcode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned long flags;
struct bcm43xx_private *bcm = dev_to_bcm(dev);
ssize_t count = 0;
u16 status;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mutex_lock(&(bcm)->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
status = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
BCM43xx_UCODE_STATUS);
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&(bcm)->mutex);
switch (status) {
case 0x0000:
count = snprintf(buf, PAGE_SIZE, "0x%.4x (invalid)\n",
status);
break;
case 0x0001:
count = snprintf(buf, PAGE_SIZE, "0x%.4x (init)\n",
status);
break;
case 0x0002:
count = snprintf(buf, PAGE_SIZE, "0x%.4x (active)\n",
status);
break;
case 0x0003:
count = snprintf(buf, PAGE_SIZE, "0x%.4x (suspended)\n",
status);
break;
case 0x0004:
count = snprintf(buf, PAGE_SIZE, "0x%.4x (asleep)\n",
status);
break;
default:
count = snprintf(buf, PAGE_SIZE, "0x%.4x (unknown)\n",
status);
break;
}
return count;
}
static DEVICE_ATTR(microcodestatus, 0444,
bcm43xx_attr_microcode_show,
NULL);
int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
{
struct device *dev = &bcm->pci_dev->dev;
int err;
assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
err = device_create_file(dev, &dev_attr_sprom);
if (err)
goto out;
err = device_create_file(dev, &dev_attr_interference);
if (err)
goto err_remove_sprom;
err = device_create_file(dev, &dev_attr_shortpreamble);
if (err)
goto err_remove_interfmode;
err = device_create_file(dev, &dev_attr_phymode);
if (err)
goto err_remove_shortpreamble;
err = device_create_file(dev, &dev_attr_microcodestatus);
if (err)
goto err_remove_phymode;
out:
return err;
err_remove_phymode:
device_remove_file(dev, &dev_attr_phymode);
err_remove_shortpreamble:
device_remove_file(dev, &dev_attr_shortpreamble);
err_remove_interfmode:
device_remove_file(dev, &dev_attr_interference);
err_remove_sprom:
device_remove_file(dev, &dev_attr_sprom);
goto out;
}
void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
{
struct device *dev = &bcm->pci_dev->dev;
device_remove_file(dev, &dev_attr_microcodestatus);
device_remove_file(dev, &dev_attr_phymode);
device_remove_file(dev, &dev_attr_shortpreamble);
device_remove_file(dev, &dev_attr_interference);
device_remove_file(dev, &dev_attr_sprom);
}
#ifndef BCM43xx_SYSFS_H_
#define BCM43xx_SYSFS_H_
struct bcm43xx_private;
int bcm43xx_sysfs_register(struct bcm43xx_private *bcm);
void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm);
#endif /* BCM43xx_SYSFS_H_ */
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <net/ieee80211softmac.h>
#include <net/ieee80211softmac_wx.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include "bcm43xx.h"
#include "bcm43xx_wx.h"
#include "bcm43xx_main.h"
#include "bcm43xx_radio.h"
#include "bcm43xx_phy.h"
/* The WIRELESS_EXT version, which is implemented by this driver. */
#define BCM43xx_WX_VERSION 18
#define MAX_WX_STRING 80
static int bcm43xx_wx_get_name(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int i;
struct bcm43xx_phyinfo *phy;
char suffix[7] = { 0 };
int have_a = 0, have_b = 0, have_g = 0;
mutex_lock(&bcm->mutex);
for (i = 0; i < bcm->nr_80211_available; i++) {
phy = &(bcm->core_80211_ext[i].phy);
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
have_a = 1;
break;
case BCM43xx_PHYTYPE_G:
have_g = 1;
case BCM43xx_PHYTYPE_B:
have_b = 1;
break;
default:
assert(0);
}
}
mutex_unlock(&bcm->mutex);
i = 0;
if (have_a) {
suffix[i++] = 'a';
suffix[i++] = '/';
}
if (have_b) {
suffix[i++] = 'b';
suffix[i++] = '/';
}
if (have_g) {
suffix[i++] = 'g';
suffix[i++] = '/';
}
if (i != 0)
suffix[i - 1] = '\0';
snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix);
return 0;
}
static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
u8 channel;
s8 expon;
int freq;
int err = -EINVAL;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if ((data->freq.e == 0) &&
(data->freq.m >= 0) && (data->freq.m <= 1000)) {
channel = data->freq.m;
freq = bcm43xx_channel_to_freq(bcm, channel);
} else {
freq = data->freq.m;
expon = 6 - data->freq.e;
while (--expon >= 0) /* scale down the frequency to MHz */
freq /= 10;
assert(freq > 1000);
channel = bcm43xx_freq_to_channel(bcm, freq);
}
if (!ieee80211_is_valid_channel(bcm->ieee, channel))
goto out_unlock;
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
//ieee80211softmac_disassoc(softmac, $REASON);
bcm43xx_mac_suspend(bcm);
err = bcm43xx_radio_selectchannel(bcm, channel, 0);
bcm43xx_mac_enable(bcm);
} else {
bcm43xx_current_radio(bcm)->initial_channel = channel;
err = 0;
}
out_unlock:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
struct bcm43xx_radioinfo *radio;
int err = -ENODEV;
u16 channel;
mutex_lock(&bcm->mutex);
radio = bcm43xx_current_radio(bcm);
channel = radio->channel;
if (channel == 0xFF) {
channel = radio->initial_channel;
if (channel == 0xFF)
goto out_unlock;
}
assert(channel > 0 && channel <= 1000);
data->freq.e = 1;
data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000;
data->freq.flags = 1;
err = 0;
out_unlock:
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_set_mode(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
int mode;
mode = data->mode;
if (mode == IW_MODE_AUTO)
mode = BCM43xx_INITIAL_IWMODE;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
if (bcm->ieee->iw_mode != mode)
bcm43xx_set_iwmode(bcm, mode);
} else
bcm->ieee->iw_mode = mode;
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_get_mode(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
mutex_lock(&bcm->mutex);
data->mode = bcm->ieee->iw_mode;
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
struct iw_range *range = (struct iw_range *)extra;
const struct ieee80211_geo *geo;
int i, j;
struct bcm43xx_phyinfo *phy;
data->data.length = sizeof(*range);
memset(range, 0, sizeof(*range));
//TODO: What about 802.11b?
/* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
range->throughput = 27 * 1000 * 1000;
range->max_qual.qual = 100;
range->max_qual.level = 146; /* set floor at -110 dBm (146 - 256) */
range->max_qual.noise = 146;
range->max_qual.updated = IW_QUAL_ALL_UPDATED;
range->avg_qual.qual = 50;
range->avg_qual.level = 0;
range->avg_qual.noise = 0;
range->avg_qual.updated = IW_QUAL_ALL_UPDATED;
range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;
range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;
range->min_frag = MIN_FRAG_THRESHOLD;
range->max_frag = MAX_FRAG_THRESHOLD;
range->encoding_size[0] = 5;
range->encoding_size[1] = 13;
range->num_encoding_sizes = 2;
range->max_encoding_tokens = WEP_KEYS;
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = BCM43xx_WX_VERSION;
range->enc_capa = IW_ENC_CAPA_WPA |
IW_ENC_CAPA_WPA2 |
IW_ENC_CAPA_CIPHER_TKIP |
IW_ENC_CAPA_CIPHER_CCMP;
mutex_lock(&bcm->mutex);
phy = bcm43xx_current_phy(bcm);
range->num_bitrates = 0;
i = 0;
if (phy->type == BCM43xx_PHYTYPE_A ||
phy->type == BCM43xx_PHYTYPE_G) {
range->num_bitrates = 8;
range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB * 500000;
range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB * 500000;
}
if (phy->type == BCM43xx_PHYTYPE_B ||
phy->type == BCM43xx_PHYTYPE_G) {
range->num_bitrates += 4;
range->bitrate[i++] = IEEE80211_CCK_RATE_1MB * 500000;
range->bitrate[i++] = IEEE80211_CCK_RATE_2MB * 500000;
range->bitrate[i++] = IEEE80211_CCK_RATE_5MB * 500000;
range->bitrate[i++] = IEEE80211_CCK_RATE_11MB * 500000;
}
geo = ieee80211_get_geo(bcm->ieee);
range->num_channels = geo->a_channels + geo->bg_channels;
j = 0;
for (i = 0; i < geo->a_channels; i++) {
if (j == IW_MAX_FREQUENCIES)
break;
range->freq[j].i = j + 1;
range->freq[j].m = geo->a[i].freq * 100000;
range->freq[j].e = 1;
j++;
}
for (i = 0; i < geo->bg_channels; i++) {
if (j == IW_MAX_FREQUENCIES)
break;
range->freq[j].i = j + 1;
range->freq[j].m = geo->bg[i].freq * 100000;
range->freq[j].e = 1;
j++;
}
range->num_frequency = j;
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_set_nick(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
size_t len;
mutex_lock(&bcm->mutex);
len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
memcpy(bcm->nick, extra, len);
bcm->nick[len] = '\0';
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_get_nick(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
size_t len;
mutex_lock(&bcm->mutex);
len = strlen(bcm->nick);
memcpy(extra, bcm->nick, len);
data->data.length = (__u16)len;
data->data.flags = 1;
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_set_rts(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
int err = -EINVAL;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (data->rts.disabled) {
bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
err = 0;
} else {
if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
bcm->rts_threshold = data->rts.value;
err = 0;
}
}
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_get_rts(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
mutex_lock(&bcm->mutex);
data->rts.value = bcm->rts_threshold;
data->rts.fixed = 0;
data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_set_frag(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
int err = -EINVAL;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (data->frag.disabled) {
bcm->ieee->fts = MAX_FRAG_THRESHOLD;
err = 0;
} else {
if (data->frag.value >= MIN_FRAG_THRESHOLD &&
data->frag.value <= MAX_FRAG_THRESHOLD) {
bcm->ieee->fts = data->frag.value & ~0x1;
err = 0;
}
}
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_get_frag(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
mutex_lock(&bcm->mutex);
data->frag.value = bcm->ieee->fts;
data->frag.fixed = 0;
data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
struct bcm43xx_radioinfo *radio;
struct bcm43xx_phyinfo *phy;
unsigned long flags;
int err = -ENODEV;
u16 maxpower;
if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
printk(KERN_ERR PFX "TX power not in dBm.\n");
return -EOPNOTSUPP;
}
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
goto out_unlock;
radio = bcm43xx_current_radio(bcm);
phy = bcm43xx_current_phy(bcm);
if (data->txpower.disabled != (!(radio->enabled))) {
if (data->txpower.disabled)
bcm43xx_radio_turn_off(bcm);
else
bcm43xx_radio_turn_on(bcm);
}
if (data->txpower.value > 0) {
/* desired and maxpower dBm values are in Q5.2 */
if (phy->type == BCM43xx_PHYTYPE_A)
maxpower = bcm->sprom.maxpower_aphy;
else
maxpower = bcm->sprom.maxpower_bgphy;
radio->txpower_desired = limit_value(data->txpower.value << 2,
0, maxpower);
bcm43xx_phy_xmitpower(bcm);
}
err = 0;
out_unlock:
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
struct bcm43xx_radioinfo *radio;
int err = -ENODEV;
mutex_lock(&bcm->mutex);
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
goto out_unlock;
radio = bcm43xx_current_radio(bcm);
/* desired dBm value is in Q5.2 */
data->txpower.value = radio->txpower_desired >> 2;
data->txpower.fixed = 1;
data->txpower.flags = IW_TXPOW_DBM;
data->txpower.disabled = !(radio->enabled);
err = 0;
out_unlock:
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err;
err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
return err;
}
static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err;
err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
return err;
}
static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err;
err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
return err;
}
static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err;
err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
return err;
}
static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
int mode, err = 0;
mode = *((int *)extra);
switch (mode) {
case 0:
mode = BCM43xx_RADIO_INTERFMODE_NONE;
break;
case 1:
mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
break;
case 2:
mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
break;
case 3:
mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
break;
default:
printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
"0 => None, 1 => Non-WLAN, 2 => WLAN, "
"3 => Auto-WLAN\n");
return -EINVAL;
}
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
if (err) {
printk(KERN_ERR PFX "Interference Mitigation not "
"supported by device\n");
}
} else {
if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
"not supported while the interface is down.\n");
err = -ENODEV;
} else
bcm43xx_current_radio(bcm)->interfmode = mode;
}
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return err;
}
static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int mode;
mutex_lock(&bcm->mutex);
mode = bcm43xx_current_radio(bcm)->interfmode;
mutex_unlock(&bcm->mutex);
switch (mode) {
case BCM43xx_RADIO_INTERFMODE_NONE:
strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);
break;
case BCM43xx_RADIO_INTERFMODE_NONWLAN:
strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);
break;
case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);
break;
default:
assert(0);
}
data->data.length = strlen(extra) + 1;
return 0;
}
static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
int on;
on = *((int *)extra);
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
bcm->short_preamble = !!on;
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int on;
mutex_lock(&bcm->mutex);
on = bcm->short_preamble;
mutex_unlock(&bcm->mutex);
if (on)
strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
else
strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
data->data.length = strlen(extra) + 1;
return 0;
}
static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
unsigned long flags;
int on;
on = *((int *)extra);
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
bcm->ieee->host_encrypt = !!on;
bcm->ieee->host_decrypt = !!on;
bcm->ieee->host_build_iv = !on;
bcm->ieee->host_strip_iv_icv = !on;
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
return 0;
}
static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int on;
mutex_lock(&bcm->mutex);
on = bcm->ieee->host_encrypt;
mutex_unlock(&bcm->mutex);
if (on)
strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
else
strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
data->data.length = strlen(extra + 1);
return 0;
}
/* Enough buffer to hold a hexdump of the sprom data. */
#define SPROM_BUFFERSIZE 512
static int sprom2hex(const u16 *sprom, char *dump)
{
int i, pos = 0;
for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
"%04X", swab16(sprom[i]) & 0xFFFF);
}
return pos + 1;
}
static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
{
char tmp[5] = { 0 };
int cnt = 0;
unsigned long parsed;
if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
return -EINVAL;
while (cnt < BCM43xx_SPROM_SIZE) {
memcpy(tmp, dump, 4);
dump += 4;
parsed = simple_strtoul(tmp, NULL, 16);
sprom[cnt++] = swab16((u16)parsed);
}
return 0;
}
static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err = -EPERM;
u16 *sprom;
unsigned long flags;
if (!capable(CAP_SYS_RAWIO))
goto out;
err = -ENOMEM;
sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
GFP_KERNEL);
if (!sprom)
goto out;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
err = -ENODEV;
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
err = bcm43xx_sprom_read(bcm, sprom);
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
if (!err)
data->data.length = sprom2hex(sprom, extra);
kfree(sprom);
out:
return err;
}
static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err = -EPERM;
u16 *sprom;
unsigned long flags;
char *input;
unsigned int len;
if (!capable(CAP_SYS_RAWIO))
goto out;
err = -ENOMEM;
sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
GFP_KERNEL);
if (!sprom)
goto out;
len = data->data.length;
extra[len - 1] = '\0';
input = strchr(extra, ':');
if (input) {
input++;
len -= input - extra;
} else
input = extra;
err = hex2sprom(sprom, input, len);
if (err)
goto out_kfree;
mutex_lock(&bcm->mutex);
spin_lock_irqsave(&bcm->irq_lock, flags);
spin_lock(&bcm->leds_lock);
err = -ENODEV;
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
err = bcm43xx_sprom_write(bcm, sprom);
spin_unlock(&bcm->leds_lock);
spin_unlock_irqrestore(&bcm->irq_lock, flags);
mutex_unlock(&bcm->mutex);
out_kfree:
kfree(sprom);
out:
return err;
}
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
struct iw_statistics *wstats;
struct ieee80211_network *network = NULL;
static int tmp_level = 0;
static int tmp_qual = 0;
unsigned long flags;
wstats = &bcm->stats.wstats;
if (!mac->associnfo.associated) {
wstats->miss.beacon = 0;
// bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?
wstats->discard.retries = 0;
// bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question
wstats->discard.nwid = 0;
// bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto
wstats->discard.code = 0;
// bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here
wstats->discard.fragment = 0;
wstats->discard.misc = 0;
wstats->qual.qual = 0;
wstats->qual.level = 0;
wstats->qual.noise = 0;
wstats->qual.updated = 7;
wstats->qual.updated |= IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
return wstats;
}
/* fill in the real statistics when iface associated */
spin_lock_irqsave(&mac->ieee->lock, flags);
list_for_each_entry(network, &mac->ieee->network_list, list) {
if (!memcmp(mac->associnfo.bssid, network->bssid, ETH_ALEN)) {
if (!tmp_level) { /* get initial values */
tmp_level = network->stats.signal;
tmp_qual = network->stats.rssi;
} else { /* smooth results */
tmp_level = (15 * tmp_level + network->stats.signal)/16;
tmp_qual = (15 * tmp_qual + network->stats.rssi)/16;
}
break;
}
}
spin_unlock_irqrestore(&mac->ieee->lock, flags);
wstats->qual.level = tmp_level;
wstats->qual.qual = 100 * tmp_qual / RX_RSSI_MAX;
wstats->qual.noise = bcm->stats.noise;
wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;
wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;
wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;
wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;
wstats->discard.misc = 0; // FIXME
wstats->miss.beacon = 0; // FIXME
return wstats;
}
#ifdef WX
# undef WX
#endif
#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
static const iw_handler bcm43xx_wx_handlers[] = {
/* Wireless Identification */
WX(SIOCGIWNAME) = bcm43xx_wx_get_name,
/* Basic operations */
WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq,
WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq,
WX(SIOCSIWMODE) = bcm43xx_wx_set_mode,
WX(SIOCGIWMODE) = bcm43xx_wx_get_mode,
/* Informative stuff */
WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams,
/* Access Point manipulation */
WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
/* 802.11 specific support */
WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick,
WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick,
/* Other parameters */
WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,
WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
WX(SIOCSIWRTS) = bcm43xx_wx_set_rts,
WX(SIOCGIWRTS) = bcm43xx_wx_get_rts,
WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag,
WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag,
WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower,
WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower,
//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry,
//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry,
/* Encoding */
WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding,
WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding,
WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext,
WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext,
/* Power saving */
//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power,
//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power,
WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
};
#undef WX
static const iw_handler bcm43xx_priv_wx_handlers[] = {
/* Set Interference Mitigation Mode. */
bcm43xx_wx_set_interfmode,
/* Get Interference Mitigation Mode. */
bcm43xx_wx_get_interfmode,
/* Enable/Disable Short Preamble mode. */
bcm43xx_wx_set_shortpreamble,
/* Get Short Preamble mode. */
bcm43xx_wx_get_shortpreamble,
/* Enable/Disable Software Encryption mode */
bcm43xx_wx_set_swencryption,
/* Get Software Encryption mode */
bcm43xx_wx_get_swencryption,
/* Write SRPROM data. */
bcm43xx_wx_sprom_write,
/* Read SPROM data. */
bcm43xx_wx_sprom_read,
};
#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0)
#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1)
#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2)
#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3)
#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4)
#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5)
#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6)
#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7)
#define PRIV_WX_DUMMY(ioctl) \
{ \
.cmd = (ioctl), \
.name = "__unused" \
}
static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
{
.cmd = PRIV_WX_SET_INTERFMODE,
.set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
.name = "set_interfmode",
},
{
.cmd = PRIV_WX_GET_INTERFMODE,
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
.name = "get_interfmode",
},
{
.cmd = PRIV_WX_SET_SHORTPREAMBLE,
.set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
.name = "set_shortpreamb",
},
{
.cmd = PRIV_WX_GET_SHORTPREAMBLE,
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
.name = "get_shortpreamb",
},
{
.cmd = PRIV_WX_SET_SWENCRYPTION,
.set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
.name = "set_swencrypt",
},
{
.cmd = PRIV_WX_GET_SWENCRYPTION,
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
.name = "get_swencrypt",
},
{
.cmd = PRIV_WX_SPROM_WRITE,
.set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
.name = "write_sprom",
},
{
.cmd = PRIV_WX_SPROM_READ,
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
.name = "read_sprom",
},
};
const struct iw_handler_def bcm43xx_wx_handlers_def = {
.standard = bcm43xx_wx_handlers,
.num_standard = ARRAY_SIZE(bcm43xx_wx_handlers),
.num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
.num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args),
.private = bcm43xx_priv_wx_handlers,
.private_args = bcm43xx_priv_wx_args,
.get_wireless_stats = bcm43xx_get_wireless_stats,
};
/*
Broadcom BCM43xx wireless driver
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
Stefano Brivio <st3@riseup.net>
Michael Buesch <mbuesch@freenet.de>
Danny van Dyk <kugelfang@gentoo.org>
Andreas Jaggi <andreas.jaggi@waterwave.ch>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef BCM43xx_WX_H_
#define BCM43xx_WX_H_
extern const struct iw_handler_def bcm43xx_wx_handlers_def;
#endif /* BCM43xx_WX_H_ */
/*
Broadcom BCM43xx wireless driver
Transmission (TX/RX) related functions.
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 "bcm43xx_xmit.h"
#include <linux/etherdevice.h>
/* Extract the bitrate out of a CCK PLCP header. */
static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4 *plcp)
{
switch (plcp->raw[0]) {
case 0x0A:
return IEEE80211_CCK_RATE_1MB;
case 0x14:
return IEEE80211_CCK_RATE_2MB;
case 0x37:
return IEEE80211_CCK_RATE_5MB;
case 0x6E:
return IEEE80211_CCK_RATE_11MB;
}
assert(0);
return 0;
}
/* Extract the bitrate out of an OFDM PLCP header. */
static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4 *plcp)
{
switch (plcp->raw[0] & 0xF) {
case 0xB:
return IEEE80211_OFDM_RATE_6MB;
case 0xF:
return IEEE80211_OFDM_RATE_9MB;
case 0xA:
return IEEE80211_OFDM_RATE_12MB;
case 0xE:
return IEEE80211_OFDM_RATE_18MB;
case 0x9:
return IEEE80211_OFDM_RATE_24MB;
case 0xD:
return IEEE80211_OFDM_RATE_36MB;
case 0x8:
return IEEE80211_OFDM_RATE_48MB;
case 0xC:
return IEEE80211_OFDM_RATE_54MB;
}
assert(0);
return 0;
}
u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate)
{
switch (bitrate) {
case IEEE80211_CCK_RATE_1MB:
return 0x0A;
case IEEE80211_CCK_RATE_2MB:
return 0x14;
case IEEE80211_CCK_RATE_5MB:
return 0x37;
case IEEE80211_CCK_RATE_11MB:
return 0x6E;
}
assert(0);
return 0;
}
u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate)
{
switch (bitrate) {
case IEEE80211_OFDM_RATE_6MB:
return 0xB;
case IEEE80211_OFDM_RATE_9MB:
return 0xF;
case IEEE80211_OFDM_RATE_12MB:
return 0xA;
case IEEE80211_OFDM_RATE_18MB:
return 0xE;
case IEEE80211_OFDM_RATE_24MB:
return 0x9;
case IEEE80211_OFDM_RATE_36MB:
return 0xD;
case IEEE80211_OFDM_RATE_48MB:
return 0x8;
case IEEE80211_OFDM_RATE_54MB:
return 0xC;
}
assert(0);
return 0;
}
static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp,
const u16 octets, const u8 bitrate,
const int ofdm_modulation)
{
__le32 *data = &(plcp->data);
__u8 *raw = plcp->raw;
if (ofdm_modulation) {
u32 val = bcm43xx_plcp_get_ratecode_ofdm(bitrate);
assert(!(octets & 0xF000));
val |= (octets << 5);
*data = cpu_to_le32(val);
} else {
u32 plen;
plen = octets * 16 / bitrate;
if ((octets * 16 % bitrate) > 0) {
plen++;
if ((bitrate == IEEE80211_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] = bcm43xx_plcp_get_ratecode_cck(bitrate);
}
}
static u8 bcm43xx_calc_fallback_rate(u8 bitrate)
{
switch (bitrate) {
case IEEE80211_CCK_RATE_1MB:
return IEEE80211_CCK_RATE_1MB;
case IEEE80211_CCK_RATE_2MB:
return IEEE80211_CCK_RATE_1MB;
case IEEE80211_CCK_RATE_5MB:
return IEEE80211_CCK_RATE_2MB;
case IEEE80211_CCK_RATE_11MB:
return IEEE80211_CCK_RATE_5MB;
case IEEE80211_OFDM_RATE_6MB:
return IEEE80211_CCK_RATE_5MB;
case IEEE80211_OFDM_RATE_9MB:
return IEEE80211_OFDM_RATE_6MB;
case IEEE80211_OFDM_RATE_12MB:
return IEEE80211_OFDM_RATE_9MB;
case IEEE80211_OFDM_RATE_18MB:
return IEEE80211_OFDM_RATE_12MB;
case IEEE80211_OFDM_RATE_24MB:
return IEEE80211_OFDM_RATE_18MB;
case IEEE80211_OFDM_RATE_36MB:
return IEEE80211_OFDM_RATE_24MB;
case IEEE80211_OFDM_RATE_48MB:
return IEEE80211_OFDM_RATE_36MB;
case IEEE80211_OFDM_RATE_54MB:
return IEEE80211_OFDM_RATE_48MB;
}
assert(0);
return 0;
}
static
__le16 bcm43xx_calc_duration_id(const struct ieee80211_hdr *wireless_header,
u8 bitrate)
{
const u16 frame_ctl = le16_to_cpu(wireless_header->frame_ctl);
__le16 duration_id = wireless_header->duration_id;
switch (WLAN_FC_GET_TYPE(frame_ctl)) {
case IEEE80211_FTYPE_DATA:
case IEEE80211_FTYPE_MGMT:
//TODO: Steal the code from ieee80211, once it is completed there.
break;
case IEEE80211_FTYPE_CTL:
/* Use the original duration/id. */
break;
default:
assert(0);
}
return duration_id;
}
static inline
u16 ceiling_div(u16 dividend, u16 divisor)
{
return ((dividend + divisor - 1) / divisor);
}
static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo *phy,
struct bcm43xx_txhdr *txhdr,
u16 *flags,
u8 bitrate,
const struct ieee80211_hdr_4addr *wlhdr)
{
u16 fctl;
u16 dur;
u8 fallback_bitrate;
int ofdm_modulation;
int fallback_ofdm_modulation;
// u8 *sa, *da;
u16 flen;
//FIXME sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr);
//FIXME da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr);
fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);
ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));
fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));
flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + IEEE80211_FCS_LEN,
bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp),
flen, bitrate,
!ieee80211_is_cck_rate(bitrate));
bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp),
flen, fallback_bitrate,
!ieee80211_is_cck_rate(fallback_bitrate));
fctl = IEEE80211_FTYPE_CTL;
fctl |= IEEE80211_STYPE_RTS;
dur = le16_to_cpu(wlhdr->duration_id);
/*FIXME: should we test for dur==0 here and let it unmodified in this case?
* The following assert checks for this case...
*/
assert(dur);
/*FIXME: The duration calculation is not really correct.
* I am not 100% sure which bitrate to use. We use the RTS rate here,
* but this is likely to be wrong.
*/
if (phy->type == BCM43xx_PHYTYPE_A) {
/* Three times SIFS */
dur += 16 * 3;
/* Add ACK duration. */
dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,
bitrate * 4);
/* Add CTS duration. */
dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,
bitrate * 4);
} else {
/* Three times SIFS */
dur += 10 * 3;
/* Add ACK duration. */
dur += ceiling_div(8 * (14 /*bytes*/) * 10,
bitrate);
/* Add CTS duration. */
dur += ceiling_div(8 * (14 /*bytes*/) * 10,
bitrate);
}
txhdr->rts_cts_frame_control = cpu_to_le16(fctl);
txhdr->rts_cts_dur = cpu_to_le16(dur);
//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3));
//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da));
memcpy(txhdr->rts_cts_mac1, wlhdr->addr1, ETH_ALEN);//FIXME!
// memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN);
*flags |= BCM43xx_TXHDRFLAG_RTSCTS;
*flags |= BCM43xx_TXHDRFLAG_RTS;
if (ofdm_modulation)
*flags |= BCM43xx_TXHDRFLAG_RTSCTS_OFDM;
if (fallback_ofdm_modulation)
*flags |= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM;
}
void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm,
struct bcm43xx_txhdr *txhdr,
const unsigned char *fragment_data,
const unsigned int fragment_len,
const int is_first_fragment,
const u16 cookie)
{
const struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
const struct ieee80211_hdr_4addr *wireless_header = (const struct ieee80211_hdr_4addr *)fragment_data;
const struct ieee80211_security *secinfo = &bcm->ieee->sec;
u8 bitrate;
u8 fallback_bitrate;
int ofdm_modulation;
int fallback_ofdm_modulation;
u16 plcp_fragment_len = fragment_len;
u16 flags = 0;
u16 control = 0;
u16 wsec_rate = 0;
u16 encrypt_frame;
const u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(wireless_header->frame_ctl));
const int is_mgt = (ftype == IEEE80211_FTYPE_MGMT);
/* Now construct the TX header. */
memset(txhdr, 0, sizeof(*txhdr));
bitrate = ieee80211softmac_suggest_txrate(bcm->softmac,
is_multicast_ether_addr(wireless_header->addr1), is_mgt);
ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));
fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);
fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));
/* Set Frame Control from 80211 header. */
txhdr->frame_control = wireless_header->frame_ctl;
/* Copy address1 from 80211 header. */
memcpy(txhdr->mac1, wireless_header->addr1, 6);
/* Set the fallback duration ID. */
txhdr->fallback_dur_id = bcm43xx_calc_duration_id((const struct ieee80211_hdr *)wireless_header,
fallback_bitrate);
/* Set the cookie (used as driver internal ID for the frame) */
txhdr->cookie = cpu_to_le16(cookie);
/* Hardware appends FCS. */
plcp_fragment_len += IEEE80211_FCS_LEN;
/* Hardware encryption. */
encrypt_frame = le16_to_cpup(&wireless_header->frame_ctl) & IEEE80211_FCTL_PROTECTED;
if (encrypt_frame && !bcm->ieee->host_encrypt) {
const struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)wireless_header;
memcpy(txhdr->wep_iv, hdr->payload, 4);
/* Hardware appends ICV. */
plcp_fragment_len += 4;
wsec_rate |= (bcm->key[secinfo->active_key].algorithm << BCM43xx_TXHDR_WSEC_ALGO_SHIFT)
& BCM43xx_TXHDR_WSEC_ALGO_MASK;
wsec_rate |= (secinfo->active_key << BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT)
& BCM43xx_TXHDR_WSEC_KEYINDEX_MASK;
}
/* Generate the PLCP header and the fallback PLCP header. */
bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp),
plcp_fragment_len,
bitrate, ofdm_modulation);
bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len,
fallback_bitrate, fallback_ofdm_modulation);
/* Set the CONTROL field */
if (ofdm_modulation)
control |= BCM43xx_TXHDRCTL_OFDM;
if (bcm->short_preamble) //FIXME: could be the other way around, please test
control |= BCM43xx_TXHDRCTL_SHORT_PREAMBLE;
control |= (phy->antenna_diversity << BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT)
& BCM43xx_TXHDRCTL_ANTENNADIV_MASK;
/* Set the FLAGS field */
if (!is_multicast_ether_addr(wireless_header->addr1) &&
!is_broadcast_ether_addr(wireless_header->addr1))
flags |= BCM43xx_TXHDRFLAG_EXPECTACK;
if (1 /* FIXME: PS poll?? */)
flags |= 0x10; // FIXME: unknown meaning.
if (fallback_ofdm_modulation)
flags |= BCM43xx_TXHDRFLAG_FALLBACKOFDM;
if (is_first_fragment)
flags |= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT;
/* Set WSEC/RATE field */
wsec_rate |= (txhdr->plcp.raw[0] << BCM43xx_TXHDR_RATE_SHIFT)
& BCM43xx_TXHDR_RATE_MASK;
/* Generate the RTS/CTS packet, if required. */
/* FIXME: We should first try with CTS-to-self,
* if we are on 80211g. If we get too many
* failures (hidden nodes), we should switch back to RTS/CTS.
*/
if (0/*FIXME txctl->use_rts_cts*/) {
bcm43xx_generate_rts(phy, txhdr, &flags,
0/*FIXME txctl->rts_cts_rate*/,
wireless_header);
}
txhdr->flags = cpu_to_le16(flags);
txhdr->control = cpu_to_le16(control);
txhdr->wsec_rate = cpu_to_le16(wsec_rate);
}
static s8 bcm43xx_rssi_postprocess(struct bcm43xx_private *bcm,
u8 in_rssi, int ofdm,
int adjust_2053, int adjust_2050)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
s32 tmp;
switch (radio->version) {
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 (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
if (in_rssi > 63)
in_rssi = 63;
tmp = radio->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 == BCM43xx_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;
}
//TODO
#if 0
static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_private *bcm,
u8 in_rssi)
{
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
s8 ret;
if (phy->type == BCM43xx_PHYTYPE_A) {
//TODO: Incomplete specs.
ret = 0;
} else
ret = bcm43xx_rssi_postprocess(bcm, in_rssi, 0, 1, 1);
return ret;
}
#endif
int bcm43xx_rx(struct bcm43xx_private *bcm,
struct sk_buff *skb,
struct bcm43xx_rxhdr *rxhdr)
{
struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
struct bcm43xx_plcp_hdr4 *plcp;
struct ieee80211_rx_stats stats;
struct ieee80211_hdr_4addr *wlhdr;
u16 frame_ctl;
int is_packet_for_us = 0;
int err = -EINVAL;
const u16 rxflags1 = le16_to_cpu(rxhdr->flags1);
const u16 rxflags2 = le16_to_cpu(rxhdr->flags2);
const u16 rxflags3 = le16_to_cpu(rxhdr->flags3);
const int is_ofdm = !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_OFDM);
if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) {
plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data + 2);
/* Skip two unknown bytes and the PLCP header. */
skb_pull(skb, 2 + sizeof(struct bcm43xx_plcp_hdr6));
} else {
plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data);
/* Skip the PLCP header. */
skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6));
}
/* The SKB contains the PAYLOAD (wireless header + data)
* at this point. The FCS at the end is stripped.
*/
memset(&stats, 0, sizeof(stats));
stats.mac_time = le16_to_cpu(rxhdr->mactime);
stats.rssi = rxhdr->rssi;
stats.signal = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm,
!!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ),
!!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ));
stats.noise = bcm->stats.noise;
if (is_ofdm)
stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp);
else
stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp);
stats.received_channel = radio->channel;
stats.mask = IEEE80211_STATMASK_SIGNAL |
IEEE80211_STATMASK_NOISE |
IEEE80211_STATMASK_RATE |
IEEE80211_STATMASK_RSSI;
if (phy->type == BCM43xx_PHYTYPE_A)
stats.freq = IEEE80211_52GHZ_BAND;
else
stats.freq = IEEE80211_24GHZ_BAND;
stats.len = skb->len;
bcm->stats.last_rx = jiffies;
if (bcm->ieee->iw_mode == IW_MODE_MONITOR) {
err = ieee80211_rx(bcm->ieee, skb, &stats);
return (err == 0) ? -EINVAL : 0;
}
wlhdr = (struct ieee80211_hdr_4addr *)(skb->data);
switch (bcm->ieee->iw_mode) {
case IW_MODE_ADHOC:
if (memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 ||
memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 ||
is_broadcast_ether_addr(wlhdr->addr1) ||
is_multicast_ether_addr(wlhdr->addr1) ||
bcm->net_dev->flags & IFF_PROMISC)
is_packet_for_us = 1;
break;
case IW_MODE_INFRA:
default:
/* When receiving multicast or broadcast packets, filter out
the packets we send ourself; we shouldn't see those */
if (memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 ||
memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 ||
(memcmp(wlhdr->addr3, bcm->net_dev->dev_addr, ETH_ALEN) &&
(is_broadcast_ether_addr(wlhdr->addr1) ||
is_multicast_ether_addr(wlhdr->addr1) ||
bcm->net_dev->flags & IFF_PROMISC)))
is_packet_for_us = 1;
break;
}
frame_ctl = le16_to_cpu(wlhdr->frame_ctl);
switch (WLAN_FC_GET_TYPE(frame_ctl)) {
case IEEE80211_FTYPE_MGMT:
ieee80211_rx_mgt(bcm->ieee, wlhdr, &stats);
break;
case IEEE80211_FTYPE_DATA:
if (is_packet_for_us) {
err = ieee80211_rx(bcm->ieee, skb, &stats);
err = (err == 0) ? -EINVAL : 0;
}
break;
case IEEE80211_FTYPE_CTL:
break;
default:
assert(0);
return -EINVAL;
}
return err;
}
#ifndef BCM43xx_XMIT_H_
#define BCM43xx_XMIT_H_
#include "bcm43xx_main.h"
#define _bcm43xx_declare_plcp_hdr(size) \
struct bcm43xx_plcp_hdr##size { \
union { \
__le32 data; \
__u8 raw[size]; \
} __attribute__((__packed__)); \
} __attribute__((__packed__))
/* struct bcm43xx_plcp_hdr4 */
_bcm43xx_declare_plcp_hdr(4);
/* struct bcm43xx_plcp_hdr6 */
_bcm43xx_declare_plcp_hdr(6);
#undef _bcm43xx_declare_plcp_hdr
/* Device specific TX header. To be prepended to TX frames. */
struct bcm43xx_txhdr {
union {
struct {
__le16 flags;
__le16 wsec_rate;
__le16 frame_control;
u16 unknown_zeroed_0;
__le16 control;
u8 wep_iv[10];
u8 unknown_wsec_tkip_data[3]; //FIXME
PAD_BYTES(3);
u8 mac1[6];
u16 unknown_zeroed_1;
struct bcm43xx_plcp_hdr4 rts_cts_fallback_plcp;
__le16 rts_cts_dur_fallback;
struct bcm43xx_plcp_hdr4 fallback_plcp;
__le16 fallback_dur_id;
PAD_BYTES(2);
__le16 cookie;
__le16 unknown_scb_stuff; //FIXME
struct bcm43xx_plcp_hdr6 rts_cts_plcp;
__le16 rts_cts_frame_control;
__le16 rts_cts_dur;
u8 rts_cts_mac1[6];
u8 rts_cts_mac2[6];
PAD_BYTES(2);
struct bcm43xx_plcp_hdr6 plcp;
} __attribute__((__packed__));
u8 raw[82];
} __attribute__((__packed__));
} __attribute__((__packed__));
/* Values/Masks for the device TX header */
#define BCM43xx_TXHDRFLAG_EXPECTACK 0x0001
#define BCM43xx_TXHDRFLAG_RTSCTS 0x0002
#define BCM43xx_TXHDRFLAG_RTS 0x0004
#define BCM43xx_TXHDRFLAG_FIRSTFRAGMENT 0x0008
#define BCM43xx_TXHDRFLAG_DESTPSMODE 0x0020
#define BCM43xx_TXHDRFLAG_RTSCTS_OFDM 0x0080
#define BCM43xx_TXHDRFLAG_FALLBACKOFDM 0x0100
#define BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM 0x0200
#define BCM43xx_TXHDRFLAG_CTS 0x0400
#define BCM43xx_TXHDRFLAG_FRAMEBURST 0x0800
#define BCM43xx_TXHDRCTL_OFDM 0x0001
#define BCM43xx_TXHDRCTL_SHORT_PREAMBLE 0x0010
#define BCM43xx_TXHDRCTL_ANTENNADIV_MASK 0x0030
#define BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT 8
#define BCM43xx_TXHDR_RATE_MASK 0x0F00
#define BCM43xx_TXHDR_RATE_SHIFT 8
#define BCM43xx_TXHDR_RTSRATE_MASK 0xF000
#define BCM43xx_TXHDR_RTSRATE_SHIFT 12
#define BCM43xx_TXHDR_WSEC_KEYINDEX_MASK 0x00F0
#define BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT 4
#define BCM43xx_TXHDR_WSEC_ALGO_MASK 0x0003
#define BCM43xx_TXHDR_WSEC_ALGO_SHIFT 0
void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm,
struct bcm43xx_txhdr *txhdr,
const unsigned char *fragment_data,
const unsigned int fragment_len,
const int is_first_fragment,
const u16 cookie);
/* RX header as received from the hardware. */
struct bcm43xx_rxhdr {
/* Frame Length. Must be generated explicitly in PIO mode. */
__le16 frame_length;
PAD_BYTES(2);
/* Flags field 1 */
__le16 flags1;
u8 rssi;
u8 signal_quality;
PAD_BYTES(2);
/* Flags field 3 */
__le16 flags3;
/* Flags field 2 */
__le16 flags2;
/* Lower 16bits of the TSF at the time the frame started. */
__le16 mactime;
PAD_BYTES(14);
} __attribute__((__packed__));
#define BCM43xx_RXHDR_FLAGS1_OFDM (1 << 0)
/*#define BCM43xx_RXHDR_FLAGS1_SIGNAL??? (1 << 3) FIXME */
#define BCM43xx_RXHDR_FLAGS1_SHORTPREAMBLE (1 << 7)
#define BCM43xx_RXHDR_FLAGS1_2053RSSIADJ (1 << 14)
#define BCM43xx_RXHDR_FLAGS2_INVALIDFRAME (1 << 0)
#define BCM43xx_RXHDR_FLAGS2_TYPE2FRAME (1 << 2)
/*FIXME: WEP related flags */
#define BCM43xx_RXHDR_FLAGS3_2050RSSIADJ (1 << 10)
/* Transmit Status as received from the hardware. */
struct bcm43xx_hwxmitstatus {
PAD_BYTES(4);
__le16 cookie;
u8 flags;
u8 cnt1:4,
cnt2:4;
PAD_BYTES(2);
__le16 seq;
__le16 unknown; //FIXME
} __attribute__((__packed__));
/* Transmit Status in CPU byteorder. */
struct bcm43xx_xmitstatus {
u16 cookie;
u8 flags;
u8 cnt1:4,
cnt2:4;
u16 seq;
u16 unknown; //FIXME
};
#define BCM43xx_TXSTAT_FLAG_AMPDU 0x10
#define BCM43xx_TXSTAT_FLAG_INTER 0x20
u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate);
u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate);
int bcm43xx_rx(struct bcm43xx_private *bcm,
struct sk_buff *skb,
struct bcm43xx_rxhdr *rxhdr);
#endif /* BCM43xx_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