diff options
author | Li YanBo <dreamfly281@gmail.com> | 2008-10-27 20:32:57 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-06 13:52:09 -0800 |
commit | 0f22aab89715916c100b5fa7d146c497d99d45ca (patch) | |
tree | c8874b46043e91df08ee8e2273a8bd74f60aa587 /drivers | |
parent | 470c5736fffa16923a3d27a0f365b691086a014d (diff) |
Staging: add agnx wireless driver
This driver is for the Airgo AGNX00 wireless chip.
From: Li YanBo <dreamfly281@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/agnx/Kconfig | 5 | ||||
-rw-r--r-- | drivers/staging/agnx/Makefile | 8 | ||||
-rw-r--r-- | drivers/staging/agnx/TODO | 22 | ||||
-rw-r--r-- | drivers/staging/agnx/agnx.h | 156 | ||||
-rw-r--r-- | drivers/staging/agnx/debug.h | 418 | ||||
-rw-r--r-- | drivers/staging/agnx/pci.c | 650 | ||||
-rw-r--r-- | drivers/staging/agnx/phy.c | 958 | ||||
-rw-r--r-- | drivers/staging/agnx/phy.h | 409 | ||||
-rw-r--r-- | drivers/staging/agnx/rf.c | 894 | ||||
-rw-r--r-- | drivers/staging/agnx/sta.c | 219 | ||||
-rw-r--r-- | drivers/staging/agnx/sta.h | 222 | ||||
-rw-r--r-- | drivers/staging/agnx/table.c | 168 | ||||
-rw-r--r-- | drivers/staging/agnx/table.h | 10 | ||||
-rw-r--r-- | drivers/staging/agnx/xmit.c | 819 | ||||
-rw-r--r-- | drivers/staging/agnx/xmit.h | 250 |
17 files changed, 5211 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5d457c96bd7..95d802e377d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -63,5 +63,7 @@ source "drivers/staging/at76_usb/Kconfig" source "drivers/staging/poch/Kconfig" +source "drivers/staging/agnx/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 71c4d53760b..704306f525a 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_USB_ATMEL) += at76_usb/ obj-$(CONFIG_POCH) += poch/ +obj-$(CONFIG_AGNX) += agnx/ diff --git a/drivers/staging/agnx/Kconfig b/drivers/staging/agnx/Kconfig new file mode 100644 index 00000000000..7f43549e36d --- /dev/null +++ b/drivers/staging/agnx/Kconfig @@ -0,0 +1,5 @@ +config AGNX + tristate "Wireless Airgo AGNX support" + depends on WLAN_80211 && MAC80211 + ---help--- + This is an experimental driver for Airgo AGNX00 wireless chip. diff --git a/drivers/staging/agnx/Makefile b/drivers/staging/agnx/Makefile new file mode 100644 index 00000000000..1216564a312 --- /dev/null +++ b/drivers/staging/agnx/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_AGNX) += agnx.o + +agnx-objs := rf.o \ + pci.o \ + xmit.o \ + table.o \ + sta.o \ + phy.o diff --git a/drivers/staging/agnx/TODO b/drivers/staging/agnx/TODO new file mode 100644 index 00000000000..89bec74318a --- /dev/null +++ b/drivers/staging/agnx/TODO @@ -0,0 +1,22 @@ +2008 7/18 + +The RX has can't receive OFDM packet correctly, +Guess it need be do RX calibrate. + + +before 2008 3/1 + +1: The RX get too much "CRC failed" pakets, it make the card work very unstable, +2: After running a while, the card will get infinity "RX Frame" and "Error" +interrupt, not know the root reason so far, try to fix it +3: Using two tx queue txd and txm but not only txm. +4: Set the hdr correctly. +5: Try to do recalibrate correvtly +6: To support G mode in future +7: Fix the mac address can't be readed and set correctly in BE machine. +8: Fix include and exclude FCS in promisous mode and manage mode +9: Using sta_notify to notice sta change +10: Turn on frame reception at the end of start +11: Guess the card support HW_MULTICAST_FILTER +12: The tx process should be implment atomic? +13: Using mac80211 function to control the TX&RX LED. diff --git a/drivers/staging/agnx/agnx.h b/drivers/staging/agnx/agnx.h new file mode 100644 index 00000000000..6f89b9bf796 --- /dev/null +++ b/drivers/staging/agnx/agnx.h @@ -0,0 +1,156 @@ +#ifndef AGNX_H_ +#define AGNX_H_ + +#include "xmit.h" + +#define PFX KBUILD_MODNAME ": " + +static inline u32 agnx_read32(void __iomem *mem_region, u32 offset) +{ + return ioread32(mem_region + offset); +} + +static inline void agnx_write32(void __iomem *mem_region, u32 offset, u32 val) +{ + iowrite32(val, mem_region + offset); +} + +/* static const struct ieee80211_rate agnx_rates_80211b[] = { */ +/* { .rate = 10, */ +/* .val = 0xa, */ +/* .flags = IEEE80211_RATE_CCK }, */ +/* { .rate = 20, */ +/* .val = 0x14, */ +/* .hw_value = -0x14, */ +/* .flags = IEEE80211_RATE_CCK_2 }, */ +/* { .rate = 55, */ +/* .val = 0x37, */ +/* .val2 = -0x37, */ +/* .flags = IEEE80211_RATE_CCK_2 }, */ +/* { .rate = 110, */ +/* .val = 0x6e, */ +/* .val2 = -0x6e, */ +/* .flags = IEEE80211_RATE_CCK_2 } */ +/* }; */ + + +static const struct ieee80211_rate agnx_rates_80211g[] = { +/* { .bitrate = 10, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ +/* { .bitrate = 20, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ +/* { .bitrate = 55, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ +/* { .bitrate = 110, .hw_value = 4, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ + { .bitrate = 10, .hw_value = 1, }, + { .bitrate = 20, .hw_value = 2, }, + { .bitrate = 55, .hw_value = 3, }, + { .bitrate = 110, .hw_value = 4,}, + + { .bitrate = 60, .hw_value = 0xB, }, + { .bitrate = 90, .hw_value = 0xF, }, + { .bitrate = 120, .hw_value = 0xA }, + { .bitrate = 180, .hw_value = 0xE, }, +// { .bitrate = 240, .hw_value = 0xd, }, + { .bitrate = 360, .hw_value = 0xD, }, + { .bitrate = 480, .hw_value = 0x8, }, + { .bitrate = 540, .hw_value = 0xC, }, +}; + +static const struct ieee80211_channel agnx_channels[] = { + { .center_freq = 2412, .hw_value = 1, }, + { .center_freq = 2417, .hw_value = 2, }, + { .center_freq = 2422, .hw_value = 3, }, + { .center_freq = 2427, .hw_value = 4, }, + { .center_freq = 2432, .hw_value = 5, }, + { .center_freq = 2437, .hw_value = 6, }, + { .center_freq = 2442, .hw_value = 7, }, + { .center_freq = 2447, .hw_value = 8, }, + { .center_freq = 2452, .hw_value = 9, }, + { .center_freq = 2457, .hw_value = 10, }, + { .center_freq = 2462, .hw_value = 11, }, + { .center_freq = 2467, .hw_value = 12, }, + { .center_freq = 2472, .hw_value = 13, }, + { .center_freq = 2484, .hw_value = 14, }, +}; + +#define NUM_DRIVE_MODES 2 +/* Agnx operate mode */ +enum { + AGNX_MODE_80211A, + AGNX_MODE_80211A_OOB, + AGNX_MODE_80211A_MIMO, + AGNX_MODE_80211B_SHORT, + AGNX_MODE_80211B_LONG, + AGNX_MODE_80211G, + AGNX_MODE_80211G_OOB, + AGNX_MODE_80211G_MIMO, +}; + +enum { + AGNX_UNINIT, + AGNX_START, + AGNX_STOP, +}; + +struct agnx_priv { + struct pci_dev *pdev; + struct ieee80211_hw *hw; + + spinlock_t lock; + struct mutex mutex; + unsigned int init_status; + + void __iomem *ctl; /* pointer to base ram address */ + void __iomem *data; /* pointer to mem region #2 */ + + struct agnx_ring rx; + struct agnx_ring txm; + struct agnx_ring txd; + + /* Need volatile? */ + u32 irq_status; + + struct delayed_work periodic_work; /* Periodic tasks like recalibrate*/ + struct ieee80211_low_level_stats stats; + +// unsigned int phymode; + int mode; + int channel; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + + u8 mac_addr[ETH_ALEN]; + u8 revid; + + struct ieee80211_supported_band band; +}; + + +#define AGNX_CHAINS_MAX 6 +#define AGNX_PERIODIC_DELAY 60000 /* unit: ms */ +#define LOCAL_STAID 0 /* the station entry for the card itself */ +#define BSSID_STAID 1 /* the station entry for the bsssid AP */ +#define spi_delay() udelay(40) +#define eeprom_delay() udelay(40) +#define routing_table_delay() udelay(50) + +/* PDU pool MEM region #2 */ +#define AGNX_PDUPOOL 0x40000 /* PDU pool */ +#define AGNX_PDUPOOL_SIZE 0x8000 /* PDU pool size*/ +#define AGNX_PDU_TX_WQ 0x41000 /* PDU list TX workqueue */ +#define AGNX_PDU_FREE 0x41800 /* Free Pool */ +#define PDU_SIZE 0x80 /* Free Pool node size */ +#define PDU_FREE_CNT 0xd0 /* Free pool node count */ + + +/* RF stuffs */ +extern void rf_chips_init(struct agnx_priv *priv); +extern void spi_rc_write(void __iomem *mem_region, u32 chip_ids, u32 sw); +extern void calibrate_oscillator(struct agnx_priv *priv); +extern void do_calibration(struct agnx_priv *priv); +extern void antenna_calibrate(struct agnx_priv *priv); +extern void __antenna_calibrate(struct agnx_priv *priv); +extern void print_offsets(struct agnx_priv *priv); +extern int agnx_set_channel(struct agnx_priv *priv, unsigned int channel); + + +#endif /* AGNX_H_ */ diff --git a/drivers/staging/agnx/debug.h b/drivers/staging/agnx/debug.h new file mode 100644 index 00000000000..e3e25dda0ba --- /dev/null +++ b/drivers/staging/agnx/debug.h @@ -0,0 +1,418 @@ +#ifndef AGNX_DEBUG_H_ +#define AGNX_DEBUG_H_ + +#include "agnx.h" +#include "phy.h" +#include "sta.h" +#include "xmit.h" + +#define AGNX_TRACE printk(KERN_ERR PFX "function:%s line:%d\n", __func__, __LINE__) + +#define PRINTK_LE16(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.4x\n", le16_to_cpu(var)) +#define PRINTK_LE32(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.8x\n", le32_to_cpu(var)) +#define PRINTK_U8(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.2x\n", var) +#define PRINTK_BE16(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.4x\n", be16_to_cpu(var)) +#define PRINTK_BE32(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.8x\n", be32_to_cpu(var)) +#define PRINTK_BITS(prefix, field) printk(KERN_DEBUG PFX #prefix ": " #field ": 0x%x\n", (reg & field) >> field##_SHIFT) + +static inline void agnx_bug(char *reason) +{ + printk(KERN_ERR PFX "%s\n", reason); + BUG(); +} + +static inline void agnx_print_desc(struct agnx_desc *desc) +{ + u32 reg = be32_to_cpu(desc->frag); + + PRINTK_BITS(DESC, PACKET_LEN); + + if (reg & FIRST_FRAG) { + PRINTK_BITS(DESC, FIRST_PACKET_MASK); + PRINTK_BITS(DESC, FIRST_RESERV2); + PRINTK_BITS(DESC, FIRST_TKIP_ERROR); + PRINTK_BITS(DESC, FIRST_TKIP_PACKET); + PRINTK_BITS(DESC, FIRST_RESERV1); + PRINTK_BITS(DESC, FIRST_FRAG_LEN); + } else { + PRINTK_BITS(DESC, SUB_RESERV2); + PRINTK_BITS(DESC, SUB_TKIP_ERROR); + PRINTK_BITS(DESC, SUB_TKIP_PACKET); + PRINTK_BITS(DESC, SUB_RESERV1); + PRINTK_BITS(DESC, SUB_FRAG_LEN); + } + + PRINTK_BITS(DESC, FIRST_FRAG); + PRINTK_BITS(DESC, LAST_FRAG); + PRINTK_BITS(DESC, OWNER); +} + + +static inline void dump_ieee80211b_phy_hdr(__be32 _11b0, __be32 _11b1) +{ + +} + +static inline void agnx_print_hdr(struct agnx_hdr *hdr) +{ + u32 reg; + int i; + + reg = be32_to_cpu(hdr->reg0); + PRINTK_BITS(HDR, RTS); + PRINTK_BITS(HDR, MULTICAST); + PRINTK_BITS(HDR, ACK); + PRINTK_BITS(HDR, TM); + PRINTK_BITS(HDR, RELAY); + PRINTK_BITS(HDR, REVISED_FCS); + PRINTK_BITS(HDR, NEXT_BUFFER_ADDR); + + reg = be32_to_cpu(hdr->reg1); + PRINTK_BITS(HDR, MAC_HDR_LEN); + PRINTK_BITS(HDR, DURATION_OVERIDE); + PRINTK_BITS(HDR, PHY_HDR_OVERIDE); + PRINTK_BITS(HDR, CRC_FAIL); + PRINTK_BITS(HDR, SEQUENCE_NUMBER); + PRINTK_BITS(HDR, BUFF_HEAD_ADDR); + + reg = be32_to_cpu(hdr->reg2); + PRINTK_BITS(HDR, PDU_COUNT); + PRINTK_BITS(HDR, WEP_KEY); + PRINTK_BITS(HDR, USES_WEP_KEY); + PRINTK_BITS(HDR, KEEP_ALIVE); + PRINTK_BITS(HDR, BUFF_TAIL_ADDR); + + reg = be32_to_cpu(hdr->reg3); + PRINTK_BITS(HDR, CTS_11G); + PRINTK_BITS(HDR, RTS_11G); + PRINTK_BITS(HDR, FRAG_SIZE); + PRINTK_BITS(HDR, PAYLOAD_LEN); + PRINTK_BITS(HDR, FRAG_NUM); + + reg = be32_to_cpu(hdr->reg4); + PRINTK_BITS(HDR, RELAY_STAID); + PRINTK_BITS(HDR, STATION_ID); + PRINTK_BITS(HDR, WORKQUEUE_ID); + + reg = be32_to_cpu(hdr->reg5); + /* printf the route flag */ + PRINTK_BITS(HDR, ROUTE_HOST); + PRINTK_BITS(HDR, ROUTE_CARD_CPU); + PRINTK_BITS(HDR, ROUTE_ENCRYPTION); + PRINTK_BITS(HDR, ROUTE_TX); + PRINTK_BITS(HDR, ROUTE_RX1); + PRINTK_BITS(HDR, ROUTE_RX2); + PRINTK_BITS(HDR, ROUTE_COMPRESSION); + + PRINTK_BE32(HDR, hdr->_11g0); + PRINTK_BE32(HDR, hdr->_11g1); + PRINTK_BE32(HDR, hdr->_11b0); + PRINTK_BE32(HDR, hdr->_11b1); + + dump_ieee80211b_phy_hdr(hdr->_11b0, hdr->_11b1); + + /* Fixme */ + for (i = 0; i < ARRAY_SIZE(hdr->mac_hdr); i++) { + if (i == 0) + printk(KERN_DEBUG PFX "IEEE80211 HDR: "); + printk("%.2x ", hdr->mac_hdr[i]); + if (i + 1 == ARRAY_SIZE(hdr->mac_hdr)) + printk("\n"); + } + + PRINTK_BE16(HDR, hdr->rts_duration); + PRINTK_BE16(HDR, hdr->last_duration); + PRINTK_BE16(HDR, hdr->sec_last_duration); + PRINTK_BE16(HDR, hdr->other_duration); + PRINTK_BE16(HDR, hdr->tx_other_duration); + PRINTK_BE16(HDR, hdr->last_11g_len); + PRINTK_BE16(HDR, hdr->other_11g_len); + PRINTK_BE16(HDR, hdr->last_11b_len); + PRINTK_BE16(HDR, hdr->other_11b_len); + + /* FIXME */ + reg = be16_to_cpu(hdr->reg6); + PRINTK_BITS(HDR, MBF); + PRINTK_BITS(HDR, RSVD4); + + PRINTK_BE16(HDR, hdr->rx_frag_stat); + + PRINTK_BE32(HDR, hdr->time_stamp); + PRINTK_BE32(HDR, hdr->phy_stats_hi); + PRINTK_BE32(HDR, hdr->phy_stats_lo); + PRINTK_BE32(HDR, hdr->mic_key0); + PRINTK_BE32(HDR, hdr->mic_key1); +} /* agnx_print_hdr */ + + +static inline void agnx_print_rx_hdr(struct agnx_hdr *hdr) +{ + agnx_print_hdr(hdr); + + PRINTK_BE16(HDR, hdr->rx.rx_packet_duration); + PRINTK_BE16(HDR, hdr->rx.replay_cnt); + + PRINTK_U8(HDR, hdr->rx_channel); +} + +static inline void agnx_print_tx_hdr(struct agnx_hdr *hdr) +{ + agnx_print_hdr(hdr); + + PRINTK_U8(HDR, hdr->tx.long_retry_limit); + PRINTK_U8(HDR, hdr->tx.short_retry_limit); + PRINTK_U8(HDR, hdr->tx.long_retry_cnt); + PRINTK_U8(HDR, hdr->tx.short_retry_cnt); + + PRINTK_U8(HDR, hdr->rx_channel); +} + +static inline void +agnx_print_sta_power(struct agnx_priv *priv, unsigned int sta_idx) +{ + struct agnx_sta_power power; + u32 reg; + + get_sta_power(priv, &power, sta_idx); + + reg = le32_to_cpu(power.reg); + PRINTK_BITS(STA_POWER, SIGNAL); + PRINTK_BITS(STA_POWER, RATE); + PRINTK_BITS(STA_POWER, TIFS); + PRINTK_BITS(STA_POWER, EDCF); + PRINTK_BITS(STA_POWER, CHANNEL_BOND); + PRINTK_BITS(STA_POWER, PHY_MODE); + PRINTK_BITS(STA_POWER, POWER_LEVEL); + PRINTK_BITS(STA_POWER, NUM_TRANSMITTERS); +} + +static inline void +agnx_print_sta_tx_wq(struct agnx_priv *priv, unsigned int sta_idx, unsigned int wq_idx) +{ + struct agnx_sta_tx_wq tx_wq; + u32 reg; + + get_sta_tx_wq(priv, &tx_wq, sta_idx, wq_idx); + + reg = le32_to_cpu(tx_wq.reg0); + PRINTK_BITS(STA_TX_WQ, TAIL_POINTER); + PRINTK_BITS(STA_TX_WQ, HEAD_POINTER_LOW); + + reg = le32_to_cpu(tx_wq.reg3); + PRINTK_BITS(STA_TX_WQ, HEAD_POINTER_HIGH); + PRINTK_BITS(STA_TX_WQ, ACK_POINTER_LOW); + + reg = le32_to_cpu(tx_wq.reg1); + PRINTK_BITS(STA_TX_WQ, ACK_POINTER_HIGH); + PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_TAIL_PACK_CNT); + PRINTK_BITS(STA_TX_WQ, ACK_TIMOUT_TAIL_PACK_CNT); + + reg = le32_to_cpu(tx_wq.reg2); + PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_WIN_LIM_BYTE_CNT); + PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_WIN_LIM_FRAG_CNT); + PRINTK_BITS(STA_TX_WQ, WORK_QUEUE_ACK_TYPE); + PRINTK_BITS(STA_TX_WQ, WORK_QUEUE_VALID); +} + +static inline void agnx_print_sta_traffic(struct agnx_sta_traffic *traffic) +{ + u32 reg; + + reg = le32_to_cpu(traffic->reg0); + PRINTK_BITS(STA_TRAFFIC, ACK_TIMOUT_CNT); + PRINTK_BITS(STA_TRAFFIC, TRAFFIC_ACK_TYPE); + PRINTK_BITS(STA_TRAFFIC, NEW_PACKET); + PRINTK_BITS(STA_TRAFFIC, TRAFFIC_VALID); + PRINTK_BITS(STA_TRAFFIC, RX_HDR_DESC_POINTER); + + reg = le32_to_cpu(traffic->reg1); + PRINTK_BITS(STA_TRAFFIC, RX_PACKET_TIMESTAMP); + PRINTK_BITS(STA_TRAFFIC, TRAFFIC_RESERVED); + PRINTK_BITS(STA_TRAFFIC, SV); + PRINTK_BITS(STA_TRAFFIC, RX_SEQUENCE_NUM); + + PRINTK_LE32(STA_TRAFFIC, traffic->tx_replay_cnt_low); + + PRINTK_LE16(STA_TRAFFIC, traffic->tx_replay_cnt_high); + PRINTK_LE16(STA_TRAFFIC, traffic->rx_replay_cnt_high); + + PRINTK_LE32(STA_TRAFFIC, traffic->rx_replay_cnt_low); +} + +static inline void agnx_print_sta(struct agnx_priv *priv, unsigned int sta_idx) +{ + struct agnx_sta station; + struct agnx_sta *sta = &station; + u32 reg; + unsigned int i; + + get_sta(priv, sta, sta_idx); + + for (i = 0; i < 4; i++) + PRINTK_LE32(STA, sta->tx_session_keys[i]); + for (i = 0; i < 4; i++) + PRINTK_LE32(STA, sta->rx_session_keys[i]); + + reg = le32_to_cpu(sta->reg); + PRINTK_BITS(STA, ID_1); + PRINTK_BITS(STA, ID_0); + PRINTK_BITS(STA, ENABLE_CONCATENATION); + PRINTK_BITS(STA, ENABLE_DECOMPRESSION); + PRINTK_BITS(STA, STA_RESERVED); + PRINTK_BITS(STA, EAP); + PRINTK_BITS(STA, ED_NULL); + PRINTK_BITS(STA, ENCRYPTION_POLICY); + PRINTK_BITS(STA, DEFINED_KEY_ID); + PRINTK_BITS(STA, FIXED_KEY); + PRINTK_BITS(STA, KEY_VALID); + PRINTK_BITS(STA, STATION_VALID); + + PRINTK_LE32(STA, sta->tx_aes_blks_unicast); + PRINTK_LE32(STA, sta->rx_aes_blks_unicast); + + PRINTK_LE16(STA, sta->aes_format_err_unicast_cnt); + PRINTK_LE16(STA, sta->aes_replay_unicast); + + PRINTK_LE16(STA, sta->aes_decrypt_err_unicast); + PRINTK_LE16(STA, sta->aes_decrypt_err_default); + + PRINTK_LE16(STA, sta->single_retry_packets); + PRINTK_LE16(STA, sta->failed_tx_packets); + + PRINTK_LE16(STA, sta->muti_retry_packets); + PRINTK_LE16(STA, sta->ack_timeouts); + + PRINTK_LE16(STA, sta->frag_tx_cnt); + PRINTK_LE16(STA, sta->rts_brq_sent); + + PRINTK_LE16(STA, sta->tx_packets); + PRINTK_LE16(STA, sta->cts_back_timeout); + + PRINTK_LE32(STA, sta->phy_stats_high); + PRINTK_LE32(STA, sta->phy_stats_low); + +// for (i = 0; i < 8; i++) + agnx_print_sta_traffic(sta->traffic + 0); + + PRINTK_LE16(STA, sta->traffic_class0_frag_success); + PRINTK_LE16(STA, sta->traffic_class1_frag_success); + PRINTK_LE16(STA, sta->traffic_class2_frag_success); + PRINTK_LE16(STA, sta->traffic_class3_frag_success); + PRINTK_LE16(STA, sta->traffic_class4_frag_success); + PRINTK_LE16(STA, sta->traffic_class5_frag_success); + PRINTK_LE16(STA, sta->traffic_class6_frag_success); + PRINTK_LE16(STA, sta->traffic_class7_frag_success); + + PRINTK_LE16(STA, sta->num_frag_non_prime_rates); + PRINTK_LE16(STA, sta->ack_timeout_non_prime_rates); +} + + +static inline void dump_ieee80211_hdr(struct ieee80211_hdr *hdr, char *tag) +{ + u16 fctl; + int hdrlen; + DECLARE_MAC_BUF(mac); + + fctl = le16_to_cpu(hdr->frame_control); + switch (fctl & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + printk(PFX "%s DATA ", tag); + break; + case IEEE80211_FTYPE_CTL: + printk(PFX "%s CTL ", tag); + break; + case IEEE80211_FTYPE_MGMT: + printk(PFX "%s MGMT ", tag); + switch(fctl & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_ASSOC_REQ: + printk("SubType: ASSOC_REQ "); + break; + case IEEE80211_STYPE_ASSOC_RESP: + printk("SubType: ASSOC_RESP "); + break; + case IEEE80211_STYPE_REASSOC_REQ: + printk("SubType: REASSOC_REQ "); + break; + case IEEE80211_STYPE_REASSOC_RESP: + printk("SubType: REASSOC_RESP "); + break; + case IEEE80211_STYPE_PROBE_REQ: + printk("SubType: PROBE_REQ "); + break; + case IEEE80211_STYPE_PROBE_RESP: + printk("SubType: PROBE_RESP "); + break; + case IEEE80211_STYPE_BEACON: + printk("SubType: BEACON "); + break; + case IEEE80211_STYPE_ATIM: + printk("SubType: ATIM "); + break; + case IEEE80211_STYPE_DISASSOC: + printk("SubType: DISASSOC "); + break; + case IEEE80211_STYPE_AUTH: + printk("SubType: AUTH "); + break; + case IEEE80211_STYPE_DEAUTH: + printk("SubType: DEAUTH "); + break; + case IEEE80211_STYPE_ACTION: + printk("SubType: ACTION "); + break; + default: + printk("SubType: Unknow\n"); + } + break; + default: + printk(PFX "%s Packet type: Unknow\n", tag); + } + + hdrlen = ieee80211_hdrlen(fctl); + + if (hdrlen >= 4) + printk("FC=0x%04x DUR=0x%04x", + fctl, le16_to_cpu(hdr->duration_id)); + if (hdrlen >= 10) + printk(" A1=%s", print_mac(mac, hdr->addr1)); + if (hdrlen >= 16) + printk(" A2=%s", print_mac(mac, hdr->addr2)); + if (hdrlen >= 24) + printk(" A3=%s", print_mac(mac, hdr->addr3)); + if (hdrlen >= 30) + printk(" A4=%s", print_mac(mac, hdr->addr4)); + printk("\n"); +} + +static inline void dump_txm_registers(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int i; + for (i = 0; i <=0x1e8; i += 4) { + printk(KERN_DEBUG PFX "TXM: %x---> 0x%.8x\n", i, ioread32(ctl + i)); + } +} +static inline void dump_rxm_registers(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int i; + for (i = 0; i <=0x108; i += 4) + printk(KERN_DEBUG PFX "RXM: %x---> 0x%.8x\n", i, ioread32(ctl + 0x2000 + i)); +} +static inline void dump_bm_registers(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int i; + for (i = 0; i <=0x90; i += 4) + printk(KERN_DEBUG PFX "BM: %x---> 0x%.8x\n", i, ioread32(ctl + 0x2c00 + i)); +} +static inline void dump_cir_registers(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int i; + for (i = 0; i <=0xb8; i += 4) + printk(KERN_DEBUG PFX "CIR: %x---> 0x%.8x\n", i, ioread32(ctl + 0x3000 + i)); +} + +#endif /* AGNX_DEBUG_H_ */ diff --git a/drivers/staging/agnx/pci.c b/drivers/staging/agnx/pci.c new file mode 100644 index 00000000000..2e3a8d30fa9 --- /dev/null +++ b/drivers/staging/agnx/pci.c @@ -0,0 +1,650 @@ +/** + * Airgo MIMO wireless driver + * + * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> + + * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer + * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/etherdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> + +#include "agnx.h" +#include "debug.h" +#include "xmit.h" +#include "phy.h" + +MODULE_AUTHOR("Li YanBo <dreamfly281@gmail.com>"); +MODULE_DESCRIPTION("Airgo MIMO PCI wireless driver"); +MODULE_LICENSE("GPL"); + +static struct pci_device_id agnx_pci_id_tbl[] __devinitdata = { + { PCI_DEVICE(0x17cb, 0x0001) }, /* Beklin F5d8010, Netgear WGM511 etc */ + { PCI_DEVICE(0x17cb, 0x0002) }, /* Netgear Wpnt511 */ + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, agnx_pci_id_tbl); + + +static inline void agnx_interrupt_ack(struct agnx_priv *priv, u32 *reason) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + if ( *reason & AGNX_STAT_RX ) { + /* Mark complete RX */ + reg = ioread32(ctl + AGNX_CIR_RXCTL); + reg |= 0x4; + iowrite32(reg, ctl + AGNX_CIR_RXCTL); + /* disable Rx interrupt */ + } + if ( *reason & AGNX_STAT_TX ) { + reg = ioread32(ctl + AGNX_CIR_TXDCTL); + if (reg & 0x4) { + iowrite32(reg, ctl + AGNX_CIR_TXDCTL); + *reason |= AGNX_STAT_TXD; + } + reg = ioread32(ctl + AGNX_CIR_TXMCTL); + if (reg & 0x4) { + iowrite32(reg, ctl + AGNX_CIR_TXMCTL); + *reason |= AGNX_STAT_TXM; + } + } + if ( *reason & AGNX_STAT_X ) { +/* reg = ioread32(ctl + AGNX_INT_STAT); */ +/* iowrite32(reg, ctl + AGNX_INT_STAT); */ +/* /\* FIXME reinit interrupt mask *\/ */ +/* reg = 0xc390bf9 & ~IRQ_TX_BEACON; */ +/* reg &= ~IRQ_TX_DISABLE; */ +/* iowrite32(reg, ctl + AGNX_INT_MASK); */ +/* iowrite32(0x800, ctl + AGNX_CIR_BLKCTL); */ + } +} /* agnx_interrupt_ack */ + +static irqreturn_t agnx_interrupt_handler(int irq, void *dev_id) +{ + struct ieee80211_hw *dev = dev_id; + struct agnx_priv *priv = dev->priv; + void __iomem *ctl = priv->ctl; + irqreturn_t ret = IRQ_NONE; + u32 irq_reason; + + spin_lock(&priv->lock); + +// printk(KERN_ERR PFX "Get a interrupt %s\n", __func__); + + if (priv->init_status != AGNX_START) + goto out; + + /* FiXME Here has no lock, Is this will lead to race? */ + irq_reason = ioread32(ctl + AGNX_CIR_BLKCTL); + if (!(irq_reason & 0x7)) + goto out; + + ret = IRQ_HANDLED; + priv->irq_status = ioread32(ctl + AGNX_INT_STAT); + +// printk(PFX "Interrupt reason is 0x%x\n", irq_reason); + /* Make sure the txm and txd flags don't conflict with other unknown + interrupt flag, maybe is not necessary */ + irq_reason &= 0xF; + + disable_rx_interrupt(priv); + /* TODO Make sure the card finished initialized */ + agnx_interrupt_ack(priv, &irq_reason); + + if ( irq_reason & AGNX_STAT_RX ) + handle_rx_irq(priv); + if ( irq_reason & AGNX_STAT_TXD ) + handle_txd_irq(priv); + if ( irq_reason & AGNX_STAT_TXM ) + handle_txm_irq(priv); + if ( irq_reason & AGNX_STAT_X ) + handle_other_irq(priv); + + enable_rx_interrupt(priv); +out: + spin_unlock(&priv->lock); + return ret; +} /* agnx_interrupt_handler */ + + +/* FIXME */ +static int agnx_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + AGNX_TRACE; + return _agnx_tx(dev->priv, skb); +} /* agnx_tx */ + + +static int agnx_get_mac_address(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + /* Attention! directly read the MAC or other date from EEPROM will + lead to cardbus(WGM511) lock up when write to PM PLL register */ + reg = agnx_read32(ctl, 0x3544); + udelay(40); + reg = agnx_read32(ctl, 0x354c); + udelay(50); + /* Get the mac address */ + reg = agnx_read32(ctl, 0x3544); + udelay(40); + + /* HACK */ + reg = cpu_to_le32(reg); + priv->mac_addr[0] = ((u8 *)®)[2]; + priv->mac_addr[1] = ((u8 *)®)[3]; + reg = agnx_read32(ctl, 0x3548); + udelay(50); + *((u32 *)(priv->mac_addr + 2)) = cpu_to_le32(reg); + + if (!is_valid_ether_addr(priv->mac_addr)) { + DECLARE_MAC_BUF(mbuf); + printk(KERN_WARNING PFX "read mac %s\n", print_mac(mbuf, priv->mac_addr)); + printk(KERN_WARNING PFX "Invalid hwaddr! Using random hwaddr\n"); + random_ether_addr(priv->mac_addr); + } + + return 0; +} /* agnx_get_mac_address */ + +static int agnx_alloc_rings(struct agnx_priv *priv) +{ + unsigned int len; + AGNX_TRACE; + + /* Allocate RX/TXM/TXD rings info */ + priv->rx.size = AGNX_RX_RING_SIZE; + priv->txm.size = AGNX_TXM_RING_SIZE; + priv->txd.size = AGNX_TXD_RING_SIZE; + + len = priv->rx.size + priv->txm.size + priv->txd.size; + +// priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_KERNEL); + priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_ATOMIC); + if (!priv->rx.info) + return -ENOMEM; + priv->txm.info = priv->rx.info + priv->rx.size; + priv->txd.info = priv->txm.info + priv->txm.size; + + /* Allocate RX/TXM/TXD descriptors */ + priv->rx.desc = pci_alloc_consistent(priv->pdev, sizeof(struct agnx_desc) * len, + &priv->rx.dma); + if (!priv->rx.desc) { + kfree(priv->rx.info); + return -ENOMEM; + } + + priv->txm.desc = priv->rx.desc + priv->rx.size; + priv->txm.dma = priv->rx.dma + sizeof(struct agnx_desc) * priv->rx.size; + priv->txd.desc = priv->txm.desc + priv->txm.size; + priv->txd.dma = priv->txm.dma + sizeof(struct agnx_desc) * priv->txm.size; + + return 0; +} /* agnx_alloc_rings */ + +static void rings_free(struct agnx_priv *priv) +{ + unsigned int len = priv->rx.size + priv->txm.size + priv->txd.size; + unsigned long flags; + AGNX_TRACE; + + spin_lock_irqsave(&priv->lock, flags); + kfree(priv->rx.info); + pci_free_consistent(priv->pdev, sizeof(struct agnx_desc) * len, + priv->rx.desc, priv->rx.dma); + spin_unlock_irqrestore(&priv->lock, flags); +} + + +static void agnx_periodic_work_handler(struct work_struct *work) +{ + struct agnx_priv *priv = container_of(work, struct agnx_priv, + periodic_work.work); +// unsigned long flags; + unsigned long delay; + + /* fixme: using mutex?? */ +// spin_lock_irqsave(&priv->lock, flags); + + /* TODO Recalibrate*/ +// calibrate_oscillator(priv); +// antenna_calibrate(priv); +// agnx_send_packet(priv, 997); + /* FIXME */ +/* if (debug == 3) */ +/* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); */ +/* else */ + delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); +// delay = round_jiffies(HZ * 15); + + queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); + +// spin_unlock_irqrestore(&priv->lock, flags); +} + + +static int agnx_start(struct ieee80211_hw *dev) +{ + struct agnx_priv *priv = dev->priv; + unsigned long delay; + int err = 0; + AGNX_TRACE; + + err = agnx_alloc_rings(priv); + if (err) { + printk(KERN_ERR PFX "Can't alloc RX/TXM/TXD rings\n"); + goto out; + } + err = request_irq(priv->pdev->irq, &agnx_interrupt_handler, + IRQF_SHARED, "agnx_pci", dev); + if (err) { + printk(KERN_ERR PFX "Failed to register IRQ handler\n"); + rings_free(priv); + goto out; + } + +// mdelay(500); + + might_sleep(); + agnx_hw_init(priv); + +// mdelay(500); + might_sleep(); + + priv->init_status = AGNX_START; +/* INIT_DELAYED_WORK(&priv->periodic_work, agnx_periodic_work_handler); */ +/* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); */ +/* queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); */ +out: + return err; +} /* agnx_start */ + +static void agnx_stop(struct ieee80211_hw *dev) +{ + struct agnx_priv *priv = dev->priv; + AGNX_TRACE; + + priv->init_status = AGNX_STOP; + /* make sure hardware will not generate irq */ + agnx_hw_reset(priv); + free_irq(priv->pdev->irq, dev); + flush_workqueue(priv->hw->workqueue); +// cancel_delayed_work_sync(&priv->periodic_work); + unfill_rings(priv); + rings_free(priv); +} + +static int agnx_config(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct agnx_priv *priv = dev->priv; + int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + AGNX_TRACE; + + spin_lock(&priv->lock); + /* FIXME need priv lock? */ + if (channel != priv->channel) { + priv->channel = channel; + agnx_set_channel(priv, priv->channel); + } + + spin_unlock(&priv->lock); + return 0; +} + +static int agnx_config_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_if_conf *conf) +{ + struct agnx_priv *priv = dev->priv; + void __iomem *ctl = priv->ctl; + AGNX_TRACE; + + spin_lock(&priv->lock); + + if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { +// u32 reghi, reglo; + agnx_set_bssid(priv, conf->bssid); + memcpy(priv->bssid, conf->bssid, ETH_ALEN); + hash_write(priv, conf->bssid, BSSID_STAID); + sta_init(priv, BSSID_STAID); + /* FIXME needed? */ + sta_power_init(priv, BSSID_STAID); + agnx_write32(ctl, AGNX_BM_MTSM, 0xff & ~0x1); + } + if (conf->ssid_len != priv->ssid_len || + memcmp(conf->ssid, priv->ssid, conf->ssid_len)) { + agnx_set_ssid(priv, conf->ssid, conf->ssid_len); + priv->ssid_len = conf->ssid_len; + memcpy(priv->ssid, conf->ssid, conf->ssid_len); + } + spin_unlock(&priv->lock); + return 0; +} /* agnx_config_interface */ + + +static void agnx_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, struct dev_mc_list *mclist) +{ + unsigned int new_flags = 0; + + *total_flags = new_flags; + /* TODO */ +} + +static int agnx_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct agnx_priv *priv = dev->priv; + AGNX_TRACE; + + spin_lock(&priv->lock); + /* FIXME */ + if (priv->mode != NL80211_IFTYPE_MONITOR) + return -EOPNOTSUPP; + + switch (conf->type) { + case NL80211_IFTYPE_STATION: + priv->mode = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + spin_unlock(&priv->lock); + + return 0; +} + +static void agnx_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct agnx_priv *priv = dev->priv; + AGNX_TRACE; + + /* TODO */ + priv->mode = NL80211_IFTYPE_MONITOR; +} + +static int agnx_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct agnx_priv *priv = dev->priv; + AGNX_TRACE; + spin_lock(&priv->lock); + /* TODO !! */ + memcpy(stats, &priv->stats, sizeof(*stats)); + spin_unlock(&priv->lock); + + return 0; +} + +static u64 agnx_get_tsft(struct ieee80211_hw *dev) +{ + void __iomem *ctl = ((struct agnx_priv *)dev->priv)->ctl; + u32 tsftl; + u64 tsft; + AGNX_TRACE; + + /* FIXME */ + tsftl = ioread32(ctl + AGNX_TXM_TIMESTAMPLO); + tsft = ioread32(ctl + AGNX_TXM_TIMESTAMPHI); + tsft <<= 32; + tsft |= tsftl; + + return tsft; +} + +static int agnx_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + struct agnx_priv *priv = dev->priv; + AGNX_TRACE; + + /* FIXME now we just using txd queue, but should using txm queue too */ + stats[0].len = (priv->txd.idx - priv->txd.idx_sent) / 2; + stats[0].limit = priv->txd.size - 2; + stats[0].count = priv->txd.idx / 2; + + return 0; +} + +static struct ieee80211_ops agnx_ops = { + .tx = agnx_tx, + .start = agnx_start, + .stop = agnx_stop, + .add_interface = agnx_add_interface, + .remove_interface = agnx_remove_interface, + .config = agnx_config, + .config_interface = agnx_config_interface, + .configure_filter = agnx_configure_filter, + .get_stats = agnx_get_stats, + .get_tx_stats = agnx_get_tx_stats, + .get_tsf = agnx_get_tsft +}; + +static void __devexit agnx_pci_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct agnx_priv *priv = dev->priv; + AGNX_TRACE; + + if (!dev) + return; + ieee80211_unregister_hw(dev); + pci_iounmap(pdev, priv->ctl); + pci_iounmap(pdev, priv->data); + pci_release_regions(pdev); + pci_disable_device(pdev); + + ieee80211_free_hw(dev); +} + +static int __devinit agnx_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *dev; + struct agnx_priv *priv; + u32 mem_addr0, mem_len0; + u32 mem_addr1, mem_len1; + int err; + DECLARE_MAC_BUF(mac); + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Can't enable new PCI device\n"); + return err; + } + + /* get pci resource */ + mem_addr0 = pci_resource_start(pdev, 0); + mem_len0 = pci_resource_len(pdev, 0); + mem_addr1 = pci_resource_start(pdev, 1); + mem_len1 = pci_resource_len(pdev, 1); + printk(KERN_DEBUG PFX "Memaddr0 is %x, length is %x\n", mem_addr0, mem_len0); + printk(KERN_DEBUG PFX "Memaddr1 is %x, length is %x\n", mem_addr1, mem_len1); + + err = pci_request_regions(pdev, "agnx-pci"); + if (err) { + printk(KERN_ERR PFX "Can't obtain PCI resource\n"); + return err; + } + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) || + pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_ERR PFX "No suitable DMA available\n"); + goto err_free_reg; + } + + pci_set_master(pdev); + printk(KERN_DEBUG PFX "pdev->irq is %d\n", pdev->irq); + + dev = ieee80211_alloc_hw(sizeof(*priv), &agnx_ops); + if (!dev) { + printk(KERN_ERR PFX "ieee80211 alloc failed\n"); + err = -ENOMEM; + goto err_free_reg; + } + /* init priv */ + priv = dev->priv; + memset(priv, 0, sizeof(*priv)); + priv->mode = NL80211_IFTYPE_MONITOR; + priv->pdev = pdev; + priv->hw = dev; + spin_lock_init(&priv->lock); + priv->init_status = AGNX_UNINIT; + + /* Map mem #1 and #2 */ + priv->ctl = pci_iomap(pdev, 0, mem_len0); +// printk(KERN_DEBUG PFX"MEM1 mapped address is 0x%p\n", priv->ctl); + if (!priv->ctl) { + printk(KERN_ERR PFX "Can't map device memory\n"); + goto err_free_dev; + } + priv->data = pci_iomap(pdev, 1, mem_len1); + printk(KERN_DEBUG PFX "MEM2 mapped address is 0x%p\n", priv->data); + if (!priv->data) { + printk(KERN_ERR PFX "Can't map device memory\n"); + goto err_iounmap2; + } + + pci_read_config_byte(pdev, PCI_REVISION_ID, &priv->revid); + + priv->band.channels = (struct ieee80211_channel *)agnx_channels; + priv->band.n_channels = ARRAY_SIZE(agnx_channels); + priv->band.bitrates = (struct ieee80211_rate *)agnx_rates_80211g; + priv->band.n_bitrates = ARRAY_SIZE(agnx_rates_80211g); + + /* Init ieee802.11 dev */ + SET_IEEE80211_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + dev->extra_tx_headroom = sizeof(struct agnx_hdr); + + /* FIXME It only include FCS in promious mode but not manage mode */ +/* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS; */ + dev->channel_change_time = 5000; + dev->max_signal = 100; + /* FIXME */ + dev->queues = 1; + + agnx_get_mac_address(priv); + + SET_IEEE80211_PERM_ADDR(dev, priv->mac_addr); + +/* /\* FIXME *\/ */ +/* for (i = 1; i < NUM_DRIVE_MODES; i++) { */ +/* err = ieee80211_register_hwmode(dev, &priv->modes[i]); */ +/* if (err) { */ +/* printk(KERN_ERR PFX "Can't register hwmode\n"); */ +/* goto err_iounmap; */ +/* } */ +/* } */ + + priv->channel = 1; + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR PFX "Can't register hardware\n"); + goto err_iounmap; + } + + agnx_hw_reset(priv); + + + printk(PFX "%s: hwaddr %s, Rev 0x%02x\n", wiphy_name(dev->wiphy), + print_mac(mac, dev->wiphy->perm_addr), priv->revid); + return 0; + + err_iounmap: + pci_iounmap(pdev, priv->data); + + err_iounmap2: + pci_iounmap(pdev, priv->ctl); + + err_free_dev: + pci_set_drvdata(pdev, NULL); + ieee80211_free_hw(dev); + + err_free_reg: + pci_release_regions(pdev); + + pci_disable_device(pdev); + return err; +} /* agnx_pci_probe*/ + +#ifdef CONFIG_PM + +static int agnx_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + AGNX_TRACE; + + ieee80211_stop_queues(dev); + agnx_stop(dev); + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int agnx_pci_resume(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + AGNX_TRACE; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + agnx_start(dev); + ieee80211_wake_queues(dev); + + return 0; +} + +#else + +#define agnx_pci_suspend NULL +#define agnx_pci_resume NULL + +#endif /* CONFIG_PM */ + + +static struct pci_driver agnx_pci_driver = { + .name = "agnx-pci", + .id_table = agnx_pci_id_tbl, + .probe = agnx_pci_probe, + .remove = __devexit_p(agnx_pci_remove), + .suspend = agnx_pci_suspend, + .resume = agnx_pci_resume, +}; + +static int __init agnx_pci_init(void) +{ + AGNX_TRACE; + return pci_register_driver(&agnx_pci_driver); +} + +static void __exit agnx_pci_exit(void) +{ + AGNX_TRACE; + pci_unregister_driver(&agnx_pci_driver); +} + + +module_init(agnx_pci_init); +module_exit(agnx_pci_exit); diff --git a/drivers/staging/agnx/phy.c b/drivers/staging/agnx/phy.c new file mode 100644 index 00000000000..625d19287e6 --- /dev/null +++ b/drivers/staging/agnx/phy.c @@ -0,0 +1,958 @@ +/** + * Airgo MIMO wireless driver + * + * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> + + * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer + * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/etherdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include "agnx.h" +#include "debug.h" +#include "phy.h" +#include "table.h" +#include "sta.h" +#include "xmit.h" + +u8 read_from_eeprom(struct agnx_priv *priv, u16 address) +{ + void __iomem *ctl = priv->ctl; + struct agnx_eeprom cmd; + u32 reg; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = EEPROM_CMD_READ << AGNX_EEPROM_COMMAND_SHIFT; + cmd.address = address; + /* Verify that the Status bit is clear */ + /* Read Command and Address are written to the Serial Interface */ + iowrite32(*(__le32 *)&cmd, ctl + AGNX_CIR_SERIALITF); + /* Wait for the Status bit to clear again */ + eeprom_delay(); + /* Read from Data */ + reg = ioread32(ctl + AGNX_CIR_SERIALITF); + + cmd = *(struct agnx_eeprom *)® + + return cmd.data; +} + +static int card_full_reset(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); + agnx_write32(ctl, AGNX_CIR_BLKCTL, 0x80); + reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); + return 0; +} + +inline void enable_power_saving(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg &= ~0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); +} + +inline void disable_power_saving(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); +} + + +void disable_receiver(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + AGNX_TRACE; + + /* FIXME Disable the receiver */ + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x0); + /* Set gain control reset */ + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); + /* Reset gain control reset */ + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); +} + + +/* Fixme this shoule be disable RX, above is enable RX */ +void enable_receiver(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + AGNX_TRACE; + + /* Set adaptive gain control discovery mode */ + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); + /* Set gain control reset */ + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); + /* Clear gain control reset */ + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); +} + +static void mac_address_set(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u8 *mac_addr = priv->mac_addr; + u32 reg; + + /* FIXME */ + reg = (mac_addr[0] << 24) | (mac_addr[1] << 16) | mac_addr[2] << 8 | mac_addr[3]; + iowrite32(reg, ctl + AGNX_RXM_MACHI); + reg = (mac_addr[4] << 8) | mac_addr[5]; + iowrite32(reg, ctl + AGNX_RXM_MACLO); +} + +static void receiver_bssid_set(struct agnx_priv *priv, u8 *bssid) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + disable_receiver(priv); + /* FIXME */ + reg = bssid[0] << 24 | (bssid[1] << 16) | (bssid[2] << 8) | bssid[3]; + iowrite32(reg, ctl + AGNX_RXM_BSSIDHI); + reg = (bssid[4] << 8) | bssid[5]; + iowrite32(reg, ctl + AGNX_RXM_BSSIDLO); + + /* Enable the receiver */ + enable_receiver(priv); + + /* Clear the TSF */ +/* agnx_write32(ctl, AGNX_TXM_TSFLO, 0x0); */ +/* agnx_write32(ctl, AGNX_TXM_TSFHI, 0x0); */ + /* Clear the TBTT */ + agnx_write32(ctl, AGNX_TXM_TBTTLO, 0x0); + agnx_write32(ctl, AGNX_TXM_TBTTHI, 0x0); + disable_receiver(priv); +} /* receiver_bssid_set */ + +static void band_management_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + void __iomem *data = priv->data; + u32 reg; + int i; + AGNX_TRACE; + + agnx_write32(ctl, AGNX_BM_TXWADDR, AGNX_PDU_TX_WQ); + agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x0); + memset_io(data + AGNX_PDUPOOL, 0x0, AGNX_PDUPOOL_SIZE); + agnx_write32(ctl, AGNX_BM_BMCTL, 0x200); + + agnx_write32(ctl, AGNX_BM_CIPDUWCNT, 0x40); + agnx_write32(ctl, AGNX_BM_SPPDUWCNT, 0x2); + agnx_write32(ctl, AGNX_BM_RFPPDUWCNT, 0x0); + agnx_write32(ctl, AGNX_BM_RHPPDUWCNT, 0x22); + + /* FIXME Initialize the Free Pool Linked List */ + /* 1. Write the Address of the Next Node ((0x41800 + node*size)/size) + to the first word of each node. */ + for (i = 0; i < PDU_FREE_CNT; i++) { + iowrite32((AGNX_PDU_FREE + (i+1)*PDU_SIZE)/PDU_SIZE, + data + AGNX_PDU_FREE + (PDU_SIZE * i)); + /* The last node should be set to 0x0 */ + if ((i + 1) == PDU_FREE_CNT) + memset_io(data + AGNX_PDU_FREE + (PDU_SIZE * i), + 0x0, PDU_SIZE); + } + + /* Head is First Pool address (0x41800) / size (0x80) */ + agnx_write32(ctl, AGNX_BM_FPLHP, AGNX_PDU_FREE/PDU_SIZE); + /* Tail is Last Pool Address (0x47f80) / size (0x80) */ + agnx_write32(ctl, AGNX_BM_FPLTP, 0x47f80/PDU_SIZE); + /* Count is Number of Nodes in the Pool (0xd0) */ + agnx_write32(ctl, AGNX_BM_FPCNT, PDU_FREE_CNT); + + /* Start all workqueue */ + agnx_write32(ctl, AGNX_BM_CIWQCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_CPULWCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_CPUHWCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_CPUTXWCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_CPURXWCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_SPRXWCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_SPTXWCTL, 0x80000); + agnx_write32(ctl, AGNX_BM_RFPWCTL, 0x80000); + + /* Enable the Band Management */ + reg = agnx_read32(ctl, AGNX_BM_BMCTL); + reg |= 0x1; + agnx_write32(ctl, AGNX_BM_BMCTL, reg); +} /* band_managment_init */ + + +static void system_itf_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x0); + agnx_write32(ctl, AGNX_PM_TESTPHY, 0x11e143a); + + if (priv->revid == 0) { + reg = agnx_read32(ctl, AGNX_SYSITF_SYSMODE); + reg |= 0x11; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, reg); + } + /* ??? What is that means? it should difference for differice type + of cards */ + agnx_write32(ctl, AGNX_CIR_SERIALITF, 0xfff81006); + + agnx_write32(ctl, AGNX_SYSITF_GPIOIN, 0x1f0000); + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); + reg = agnx_read32(ctl, AGNX_SYSITF_GPIOIN); +} + +static void encryption_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + AGNX_TRACE; + + agnx_write32(ctl, AGNX_ENCRY_WEPKEY0, 0x0); + agnx_write32(ctl, AGNX_ENCRY_WEPKEY1, 0x0); + agnx_write32(ctl, AGNX_ENCRY_WEPKEY2, 0x0); + agnx_write32(ctl, AGNX_ENCRY_WEPKEY3, 0x0); + agnx_write32(ctl, AGNX_ENCRY_CCMRECTL, 0x8); +} + +static void tx_management_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + void __iomem *data = priv->data; + u32 reg; + AGNX_TRACE; + + /* Fill out the ComputationalEngineLookupTable + * starting at memory #2 offset 0x800 + */ + tx_engine_lookup_tbl_init(priv); + memset_io(data + 0x1000, 0, 0xfe0); + /* Enable Transmission Management Functions */ + agnx_write32(ctl, AGNX_TXM_ETMF, 0x3ff); + /* Write 0x3f to Transmission Template */ + agnx_write32(ctl, AGNX_TXM_TXTEMP, 0x3f); + + if (priv->revid >= 2) + agnx_write32(ctl, AGNX_TXM_SIFSPIFS, 0x1e140a0b); + else + agnx_write32(ctl, AGNX_TXM_SIFSPIFS, 0x1e190a0b); + + reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); + reg &= 0xff00; + reg |= 0xb; + agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); + reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); + reg &= 0xffff00ff; + reg |= 0xa00; + agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); + /* Enable TIFS */ + agnx_write32(ctl, AGNX_TXM_CTL, 0x40000); + + reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); + reg &= 0xff00ffff; + reg |= 0x510000; + agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); + reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); + reg &= 0xff00ffff; + agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); + reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); + reg &= 0x00ffffff; + reg |= 0x1c000000; + agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); + reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); + reg &= 0x00ffffff; + reg |= 0x01000000; + agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); + + /* # Set DIF 0-1,2-3,4-5,6-7 to defaults */ + agnx_write32(ctl, AGNX_TXM_DIF01, 0x321d321d); + agnx_write32(ctl, AGNX_TXM_DIF23, 0x321d321d); + agnx_write32(ctl, AGNX_TXM_DIF45, 0x321d321d); + agnx_write32(ctl, AGNX_TXM_DIF67, 0x321d321d); + + /* Max Ack timeout limit */ + agnx_write32(ctl, AGNX_TXM_MAXACKTIM, 0x1e19); + /* Max RX Data Timeout count, */ + reg = agnx_read32(ctl, AGNX_TXM_MAXRXTIME); + reg &= 0xffff0000; + reg |= 0xff; + agnx_write32(ctl, AGNX_TXM_MAXRXTIME, reg); + + /* CF poll RX Timeout count */ + reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); + reg &= 0xffff; + reg |= 0xff0000; + agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); + + /* Max Timeout Exceeded count, */ + reg = agnx_read32(ctl, AGNX_TXM_MAXTIMOUT); + reg &= 0xff00ffff; + reg |= 0x190000; + agnx_write32(ctl, AGNX_TXM_MAXTIMOUT, reg); + + /* CF ack timeout limit for 11b */ + reg = agnx_read32(ctl, AGNX_TXM_CFACKT11B); + reg &= 0xff00; + reg |= 0x1e; + agnx_write32(ctl, AGNX_TXM_CFACKT11B, reg); + + /* Max CF Poll Timeout Count */ + reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); + reg &= 0xffff0000; + reg |= 0x19; + agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); + /* CF Poll RX Timeout Count */ + reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); + reg &= 0xffff0000; + reg |= 0x1e; + agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); + + /* # write default to */ + /* 1. Schedule Empty Count */ + agnx_write32(ctl, AGNX_TXM_SCHEMPCNT, 0x5); + /* 2. CFP Period Count */ + agnx_write32(ctl, AGNX_TXM_CFPERCNT, 0x1); + /* 3. CFP MDV */ + agnx_write32(ctl, AGNX_TXM_CFPMDV, 0x10000); + + /* Probe Delay */ + reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); + reg &= 0xffff0000; + reg |= 0x400; + agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); + + /* Max CCA count Slot */ + reg = agnx_read32(ctl, AGNX_TXM_MAXCCACNTSLOT); + reg &= 0xffff00ff; + reg |= 0x900; + agnx_write32(ctl, AGNX_TXM_MAXCCACNTSLOT, reg); + + /* Slot limit/1 msec Limit */ + reg = agnx_read32(ctl, AGNX_TXM_SLOTLIMIT); + reg &= 0xff00ffff; + reg |= 0x140077; + agnx_write32(ctl, AGNX_TXM_SLOTLIMIT, reg); + + /* # Set CW #(0-7) to default */ + agnx_write32(ctl, AGNX_TXM_CW0, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW1, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW2, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW3, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW4, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW5, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW6, 0xff0007); + agnx_write32(ctl, AGNX_TXM_CW7, 0xff0007); + + /* # Set Short/Long limit #(0-7) to default */ + agnx_write32(ctl, AGNX_TXM_SLBEALIM0, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM1, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM2, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM3, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM4, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM5, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM6, 0xa000a); + agnx_write32(ctl, AGNX_TXM_SLBEALIM7, 0xa000a); + + reg = agnx_read32(ctl, AGNX_TXM_CTL); + reg |= 0x1400; + agnx_write32(ctl, AGNX_TXM_CTL, reg); + /* Wait for bit 0 in Control Reg to clear */ + udelay(80); + reg = agnx_read32(ctl, AGNX_TXM_CTL); + /* Or 0x18000 to Control reg */ + reg = agnx_read32(ctl, AGNX_TXM_CTL); + reg |= 0x18000; + agnx_write32(ctl, AGNX_TXM_CTL, reg); + /* Wait for bit 0 in Control Reg to clear */ + udelay(80); + reg = agnx_read32(ctl, AGNX_TXM_CTL); + + /* Set Listen Interval Count to default */ + agnx_write32(ctl, AGNX_TXM_LISINTERCNT, 0x1); + /* Set DTIM period count to default */ + agnx_write32(ctl, AGNX_TXM_DTIMPERICNT, 0x2000); +} /* tx_management_init */ + +static void rx_management_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + AGNX_TRACE; + + /* Initialize the Routing Table */ + routing_table_init(priv); + + if (priv->revid >= 3) { + agnx_write32(ctl, 0x2074, 0x1f171710); + agnx_write32(ctl, 0x2078, 0x10100d0d); + agnx_write32(ctl, 0x207c, 0x11111010); + } + else + agnx_write32(ctl, AGNX_RXM_DELAY11, 0x0); + agnx_write32(ctl, AGNX_RXM_REQRATE, 0x8195e00); +} + + +static void agnx_timer_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + AGNX_TRACE; + +/* /\* Write 0x249f00 (tick duration?) to Timer 1 *\/ */ +/* agnx_write32(ctl, AGNX_TIMCTL_TIMER1, 0x249f00); */ +/* /\* Write 0xe2 to Timer 1 Control *\/ */ +/* agnx_write32(ctl, AGNX_TIMCTL_TIM1CTL, 0xe2); */ + + /* Write 0x249f00 (tick duration?) to Timer 1 */ + agnx_write32(ctl, AGNX_TIMCTL_TIMER1, 0x0); + /* Write 0xe2 to Timer 1 Control */ + agnx_write32(ctl, AGNX_TIMCTL_TIM1CTL, 0x0); + + iowrite32(0xFFFFFFFF, priv->ctl + AGNX_TXM_BEACON_CTL); +} + +static void power_manage_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + agnx_write32(ctl, AGNX_PM_MACMSW, 0x1f); + agnx_write32(ctl, AGNX_PM_RFCTL, 0x1f); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg &= 0xf00f; + reg |= 0xa0; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + + if (priv->revid >= 3) { + reg = agnx_read32(ctl, AGNX_PM_SOFTRST); + reg |= 0x18; + agnx_write32(ctl, AGNX_PM_SOFTRST, reg); + } +} /* power_manage_init */ + + +static void gain_ctlcnt_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + agnx_write32(ctl, AGNX_GCR_TRACNT5, 0x119); + agnx_write32(ctl, AGNX_GCR_TRACNT6, 0x118); + agnx_write32(ctl, AGNX_GCR_TRACNT7, 0x117); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg &= ~0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + + agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x0); + + /* FIXME Write the initial Station Descriptor for the card */ + sta_init(priv, LOCAL_STAID); + sta_init(priv, BSSID_STAID); + + /* Enable staion 0 and 1 can do TX */ + /* It seemed if we set other bit to 1 the bit 0 will + be auto change to 0 */ + agnx_write32(ctl, AGNX_BM_TXTOPEER, 0x2 | 0x1); +// agnx_write32(ctl, AGNX_BM_TXTOPEER, 0x1); +} /* gain_ctlcnt_init */ + + +static void phy_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + void __iomem *data = priv->data; + u32 reg; + AGNX_TRACE; + + /* Load InitialGainTable */ + gain_table_init(priv); + + agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x2000000); + + /* Clear the following offsets in Memory Range #2: */ + memset_io(data + 0x5040, 0, 0xa * 4); + memset_io(data + 0x5080, 0, 0xa * 4); + memset_io(data + 0x50c0, 0, 0xa * 4); + memset_io(data + 0x5400, 0, 0x80 * 4); + memset_io(data + 0x6000, 0, 0x280 * 4); + memset_io(data + 0x7000, 0, 0x280 * 4); + memset_io(data + 0x8000, 0, 0x280 * 4); + + /* Initialize the Following Registers According to PCI Revision ID */ + if (priv->revid == 0) { + /* fixme the part hasn't been update but below has been update + based on WGM511 */ + agnx_write32(ctl, AGNX_ACI_LEN, 0xf); + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x1d); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x3); + agnx_write32(ctl, AGNX_ACI_AICCHA0OVE, 0x11); + agnx_write32(ctl, AGNX_ACI_AICCHA1OVE, 0x0); + agnx_write32(ctl, AGNX_GCR_THD0A, 0x64); + agnx_write32(ctl, AGNX_GCR_THD0AL, 0x4b); + agnx_write32(ctl, AGNX_GCR_THD0B, 0x4b); + agnx_write32(ctl, AGNX_GCR_DUNSAT, 0x14); + agnx_write32(ctl, AGNX_GCR_DSAT, 0x24); + agnx_write32(ctl, AGNX_GCR_DFIRCAL, 0x8); + agnx_write32(ctl, AGNX_GCR_DGCTL11A, 0x1a); + agnx_write32(ctl, AGNX_GCR_DGCTL11B, 0x3); + agnx_write32(ctl, AGNX_GCR_GAININIT, 0xd); + agnx_write32(ctl, AGNX_GCR_THNOSIG, 0x1); + agnx_write32(ctl, AGNX_GCR_COARSTEP, 0x7); + agnx_write32(ctl, AGNX_GCR_SIFST11A, 0x28); + agnx_write32(ctl, AGNX_GCR_SIFST11B, 0x28); + reg = agnx_read32(ctl, AGNX_GCR_CWDETEC); + reg |= 0x1; + agnx_write32(ctl, AGNX_GCR_CWDETEC, reg); + agnx_write32(ctl, AGNX_GCR_0X38, 0x1e); + agnx_write32(ctl, AGNX_GCR_BOACT, 0x26); + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); + agnx_write32(ctl, AGNX_GCR_NLISTANT, 0x3); + agnx_write32(ctl, AGNX_GCR_NACTIANT, 0x3); + agnx_write32(ctl, AGNX_GCR_NMEASANT, 0x3); + agnx_write32(ctl, AGNX_GCR_NCAPTANT, 0x3); + agnx_write32(ctl, AGNX_GCR_THCAP11A, 0x0); + agnx_write32(ctl, AGNX_GCR_THCAP11B, 0x0); + agnx_write32(ctl, AGNX_GCR_THCAPRX11A, 0x0); + agnx_write32(ctl, AGNX_GCR_THCAPRX11B, 0x0); + agnx_write32(ctl, AGNX_GCR_THLEVDRO, 0x10); + agnx_write32(ctl, AGNX_GCR_MAXRXTIME11A, 0x1); + agnx_write32(ctl, AGNX_GCR_MAXRXTIME11B, 0x1); + agnx_write32(ctl, AGNX_GCR_CORRTIME, 0x190); + agnx_write32(ctl, AGNX_GCR_SIGHTH, 0x78); + agnx_write32(ctl, AGNX_GCR_SIGLTH, 0x1c); + agnx_write32(ctl, AGNX_GCR_CORRDROP, 0x0); + agnx_write32(ctl, AGNX_GCR_THCD, 0x0); + agnx_write32(ctl, AGNX_GCR_MAXPOWDIFF, 0x1); + agnx_write32(ctl, AGNX_GCR_TESTBUS, 0x0); + agnx_write32(ctl, AGNX_GCR_ANTCFG, 0x1f); + agnx_write32(ctl, AGNX_GCR_THJUMP, 0x14); + agnx_write32(ctl, AGNX_GCR_THPOWER, 0x0); + agnx_write32(ctl, AGNX_GCR_THPOWCLIP, 0x30); + agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x32); + agnx_write32(ctl, AGNX_GCR_THRX11BPOWMIN, 0x19); + agnx_write32(ctl, AGNX_GCR_0X14c, 0x0); + agnx_write32(ctl, AGNX_GCR_0X150, 0x0); + agnx_write32(ctl, 0x9400, 0x0); + agnx_write32(ctl, 0x940c, 0x6ff); + agnx_write32(ctl, 0x9428, 0xa0); + agnx_write32(ctl, 0x9434, 0x0); + agnx_write32(ctl, 0x9c04, 0x15); + agnx_write32(ctl, 0x9c0c, 0x7f); + agnx_write32(ctl, 0x9c34, 0x0); + agnx_write32(ctl, 0xc000, 0x38d); + agnx_write32(ctl, 0x14018, 0x0); + agnx_write32(ctl, 0x16000, 0x1); + agnx_write32(ctl, 0x11004, 0x0); + agnx_write32(ctl, 0xec54, 0xa); + agnx_write32(ctl, 0xec1c, 0x5); + } else if (priv->revid > 0) { + agnx_write32(ctl, AGNX_ACI_LEN, 0xf); + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); + agnx_write32(ctl, AGNX_ACI_AICCHA0OVE, 0x11); + agnx_write32(ctl, AGNX_ACI_AICCHA1OVE, 0x0); + agnx_write32(ctl, AGNX_GCR_DUNSAT, 0x14); + agnx_write32(ctl, AGNX_GCR_DSAT, 0x24); + agnx_write32(ctl, AGNX_GCR_DFIRCAL, 0x8); + agnx_write32(ctl, AGNX_GCR_DGCTL11A, 0x1a); + agnx_write32(ctl, AGNX_GCR_DGCTL11B, 0x3); + agnx_write32(ctl, AGNX_GCR_GAININIT, 0xd); + agnx_write32(ctl, AGNX_GCR_THNOSIG, 0x1); + agnx_write32(ctl, AGNX_GCR_COARSTEP, 0x7); + agnx_write32(ctl, AGNX_GCR_SIFST11A, 0x28); + agnx_write32(ctl, AGNX_GCR_SIFST11B, 0x28); + agnx_write32(ctl, AGNX_GCR_CWDETEC, 0x0); + agnx_write32(ctl, AGNX_GCR_0X38, 0x1e); +// agnx_write32(ctl, AGNX_GCR_BOACT, 0x26); + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); + + agnx_write32(ctl, AGNX_GCR_THCAP11A, 0x32); + agnx_write32(ctl, AGNX_GCR_THCAP11B, 0x32); + agnx_write32(ctl, AGNX_GCR_THCAPRX11A, 0x32); + agnx_write32(ctl, AGNX_GCR_THCAPRX11B, 0x32); + agnx_write32(ctl, AGNX_GCR_THLEVDRO, 0x10); + agnx_write32(ctl, AGNX_GCR_MAXRXTIME11A, 0x1ad); + agnx_write32(ctl, AGNX_GCR_MAXRXTIME11B, 0xa10); + agnx_write32(ctl, AGNX_GCR_CORRTIME, 0x190); + agnx_write32(ctl, AGNX_GCR_CORRDROP, 0x0); + agnx_write32(ctl, AGNX_GCR_THCD, 0x0); + agnx_write32(ctl, AGNX_GCR_THCS, 0x0); + agnx_write32(ctl, AGNX_GCR_MAXPOWDIFF, 0x4); + agnx_write32(ctl, AGNX_GCR_TESTBUS, 0x0); + agnx_write32(ctl, AGNX_GCR_THJUMP, 0x1e); + agnx_write32(ctl, AGNX_GCR_THPOWER, 0x0); + agnx_write32(ctl, AGNX_GCR_THPOWCLIP, 0x2a); + agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x3c); + agnx_write32(ctl, AGNX_GCR_THRX11BPOWMIN, 0x19); + agnx_write32(ctl, AGNX_GCR_0X14c, 0x0); + agnx_write32(ctl, AGNX_GCR_0X150, 0x0); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + agnx_write32(ctl, AGNX_GCR_WATCHDOG, 0x37); + agnx_write32(ctl, 0x9400, 0x0); + agnx_write32(ctl, 0x940c, 0x6ff); + agnx_write32(ctl, 0x9428, 0xa0); + agnx_write32(ctl, 0x9434, 0x0); + agnx_write32(ctl, 0x9c04, 0x15); + agnx_write32(ctl, 0x9c0c, 0x7f); + agnx_write32(ctl, 0x9c34, 0x0); + agnx_write32(ctl, 0xc000, 0x38d); + agnx_write32(ctl, 0x14014, 0x1000); + agnx_write32(ctl, 0x14018, 0x0); + agnx_write32(ctl, 0x16000, 0x1); + agnx_write32(ctl, 0x11004, 0x0); + agnx_write32(ctl, 0xec54, 0xa); + agnx_write32(ctl, 0xec1c, 0x50); + } else if (priv->revid > 1) { + reg = agnx_read32(ctl, 0xec18); + reg |= 0x8; + agnx_write32(ctl, 0xec18, reg); + } + + /* Write the TX Fir Coefficient Table */ + tx_fir_table_init(priv); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg &= ~0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + reg = agnx_read32(ctl, AGNX_PM_PLLCTL); + reg |= 0x1; + agnx_write32(ctl, AGNX_PM_PLLCTL, reg); + +/* reg = agnx_read32(ctl, 0x1a030); */ +/* reg &= ~0x4; */ +/* agnx_write32(ctl, 0x1a030, reg); */ + + agnx_write32(ctl, AGNX_GCR_TRACNT4, 0x113); +} /* phy_init */ + +static void chip_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + band_management_init(priv); + + rf_chips_init(priv); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + + /* Initialize the PHY */ + phy_init(priv); + + encryption_init(priv); + + tx_management_init(priv); + + rx_management_init(priv); + + power_manage_init(priv); + + /* Initialize the Timers */ + agnx_timer_init(priv); + + /* Write 0xc390bf9 to Interrupt Mask (Disable TX) */ + reg = 0xc390bf9 & ~IRQ_TX_BEACON; + reg &= ~IRQ_TX_DISABLE; + agnx_write32(ctl, AGNX_INT_MASK, reg); + + reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); + reg |= 0x800; + agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); + + /* set it when need get multicast enable? */ + agnx_write32(ctl, AGNX_BM_MTSM, 0xff); +} /* chip_init */ + + +static inline void set_promis_and_managed(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10 | 0x2); + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10 | 0x2); +} +static inline void set_learn_mode(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x8); +} +static inline void set_scan_mode(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x20); +} +static inline void set_promiscuous_mode(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + /* agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x210);*/ + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10); +} +static inline void set_managed_mode(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x2); +} +static inline void set_adhoc_mode(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x0); +} + +static void unknow_register_write(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x0, 0x3e); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x4, 0xb2); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x8, 0x140); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0xc, 0x1C0); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x10, 0x1FF); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x14, 0x1DD); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x18, 0x15F); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x1c, 0xA1); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x20, 0x3E7); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x24, 0x36B); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x28, 0x348); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x2c, 0x37D); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x30, 0x3DE); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x34, 0x36); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x38, 0x64); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x3c, 0x57); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x40, 0x23); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x44, 0x3ED); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x48, 0x3C9); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x4c, 0x3CA); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x50, 0x3E7); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x54, 0x8); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x58, 0x1F); + agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x5c, 0x1a); +} + +static void card_interface_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u8 bssid[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + u32 reg; + unsigned int i; + AGNX_TRACE; + + might_sleep(); + /* Clear RX Control and Enable RX queues */ + agnx_write32(ctl, AGNX_CIR_RXCTL, 0x8); + + might_sleep(); + /* Do a full reset of the card */ + card_full_reset(priv); + might_sleep(); + + /* Check and set Card Endianness */ + reg = ioread32(priv->ctl + AGNX_CIR_ENDIAN); + /* TODO If not 0xB3B2B1B0 set to 0xB3B2B1B0 */ + printk(KERN_INFO PFX "CIR_ENDIAN is %x\n", reg); + + + /* Config the eeprom */ + agnx_write32(ctl, AGNX_CIR_SERIALITF, 0x7000086); + udelay(10); + reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); + + + agnx_write32(ctl, AGNX_PM_SOFTRST, 0x80000033); + reg = agnx_read32(ctl, 0xec50); + reg |= 0xf; + agnx_write32(ctl, 0xec50, reg); + agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); + + + reg = agnx_read32(ctl, AGNX_SYSITF_GPIOIN); + udelay(10); + reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); + + /* Dump the eeprom */ + do { + char eeprom[0x100000/0x100]; + + for (i = 0; i < 0x100000; i += 0x100) { + agnx_write32(ctl, AGNX_CIR_SERIALITF, 0x3000000 + i); + udelay(13); + reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); + udelay(70); + reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); + eeprom[i/0x100] = reg & 0xFF; + udelay(10); + } + print_hex_dump_bytes(PFX "EEPROM: ", DUMP_PREFIX_NONE, eeprom, + ARRAY_SIZE(eeprom)); + } while(0); + + spi_rc_write(ctl, RF_CHIP0, 0x26); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + + /* Initialize the system interface */ + system_itf_init(priv); + + might_sleep(); + /* Chip Initialization (Polaris) */ + chip_init(priv); + might_sleep(); + + /* Calibrate the antennae */ + antenna_calibrate(priv); + + reg = agnx_read32(ctl, 0xec50); + reg &= ~0x40; + agnx_write32(ctl, 0xec50, reg); + agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); + agnx_write32(ctl, AGNX_PM_PLLCTL, 0x1); + + reg = agnx_read32(ctl, AGNX_BM_BMCTL); + reg |= 0x8000; + agnx_write32(ctl, AGNX_BM_BMCTL, reg); + enable_receiver(priv); + reg = agnx_read32(ctl, AGNX_SYSITF_SYSMODE); + reg |= 0x200; + agnx_write32(ctl, AGNX_SYSITF_SYSMODE, reg); + enable_receiver(priv); + + might_sleep(); + /* Initialize Gain Control Counts */ + gain_ctlcnt_init(priv); + + /* Write Initial Station Power Template for this station(#0) */ + sta_power_init(priv, LOCAL_STAID); + + might_sleep(); + /* Initialize the rx,td,tm rings, for each node in the ring */ + fill_rings(priv); + + might_sleep(); + + + agnx_write32(ctl, AGNX_PM_SOFTRST, 0x80000033); + agnx_write32(ctl, 0xec50, 0xc); + agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); + + /* FIXME Initialize the transmit control register */ + agnx_write32(ctl, AGNX_TXM_CTL, 0x194c1); + + enable_receiver(priv); + + might_sleep(); + /* FIXME Set the Receive Control Mac Address to card address */ + mac_address_set(priv); + enable_receiver(priv); + might_sleep(); + + /* Set the recieve request rate */ + /* FIXME Enable the request */ + /* Check packet length */ + /* Set maximum packet length */ +/* agnx_write32(ctl, AGNX_RXM_REQRATE, 0x88195e00); */ +/* enable_receiver(priv); */ + + /* Set the Receiver BSSID */ + receiver_bssid_set(priv, bssid); + + /* FIXME Set to managed mode */ + set_managed_mode(priv); +// set_promiscuous_mode(priv); +/* set_scan_mode(priv); */ +/* set_learn_mode(priv); */ +// set_promis_and_managed(priv); +// set_adhoc_mode(priv); + + /* Set the recieve request rate */ + /* Check packet length */ + agnx_write32(ctl, AGNX_RXM_REQRATE, 0x08000000); + reg = agnx_read32(ctl, AGNX_RXM_REQRATE); + /* Set maximum packet length */ + reg |= 0x00195e00; + agnx_write32(ctl, AGNX_RXM_REQRATE, reg); + + /* Configure the RX and TX interrupt */ + reg = ENABLE_RX_INTERRUPT | RX_CACHE_LINE | FRAG_LEN_2048 | FRAG_BE; + agnx_write32(ctl, AGNX_CIR_RXCFG, reg); + /* FIXME */ + reg = ENABLE_TX_INTERRUPT | TX_CACHE_LINE | FRAG_LEN_2048 | FRAG_BE; + agnx_write32(ctl, AGNX_CIR_TXCFG, reg); + + /* Enable RX TX Interrupts */ + agnx_write32(ctl, AGNX_CIR_RXCTL, 0x80); + agnx_write32(ctl, AGNX_CIR_TXMCTL, 0x80); + agnx_write32(ctl, AGNX_CIR_TXDCTL, 0x80); + + /* FIXME Set the master control interrupt in block control */ + agnx_write32(ctl, AGNX_CIR_BLKCTL, 0x800); + + /* Enable RX and TX queues */ + reg = agnx_read32(ctl, AGNX_CIR_RXCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_CIR_RXCTL, reg); + reg = agnx_read32(ctl, AGNX_CIR_TXMCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_CIR_TXMCTL, reg); + reg = agnx_read32(ctl, AGNX_CIR_TXDCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_CIR_TXDCTL, reg); + + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); + /* FIXME */ + /* unknow_register_write(priv); */ + /* Update local card hash entry */ + hash_write(priv, priv->mac_addr, LOCAL_STAID); + + might_sleep(); + + /* FIXME */ + agnx_set_channel(priv, 1); + might_sleep(); +} /* agnx_card_interface_init */ + + +void agnx_hw_init(struct agnx_priv *priv) +{ + AGNX_TRACE; + might_sleep(); + card_interface_init(priv); +} + +int agnx_hw_reset(struct agnx_priv *priv) +{ + return card_full_reset(priv); +} + +int agnx_set_ssid(struct agnx_priv *priv, u8 *ssid, size_t ssid_len) +{ + AGNX_TRACE; + return 0; +} + +void agnx_set_bssid(struct agnx_priv *priv, u8 *bssid) +{ + receiver_bssid_set(priv, bssid); +} diff --git a/drivers/staging/agnx/phy.h b/drivers/staging/agnx/phy.h new file mode 100644 index 00000000000..55e1e222179 --- /dev/null +++ b/drivers/staging/agnx/phy.h @@ -0,0 +1,409 @@ +#ifndef AGNX_PHY_H_ +#define AGNX_PHY_H_ + +#include "agnx.h" + +/* Transmission Managment Registers */ +#define AGNX_TXM_BASE 0x0000 +#define AGNX_TXM_CTL 0x0000 /* control register */ +#define AGNX_TXM_ETMF 0x0004 /* enable transmission management functions */ +#define AGNX_TXM_TXTEMP 0x0008 /* transmission template */ +#define AGNX_TXM_RETRYSTAID 0x000c /* Retry Station ID */ +#define AGNX_TXM_TIMESTAMPLO 0x0010 /* Timestamp Lo */ +#define AGNX_TXM_TIMESTAMPHI 0x0014 /* Timestamp Hi */ +#define AGNX_TXM_TXDELAY 0x0018 /* tx delay */ +#define AGNX_TXM_TBTTLO 0x0020 /* tbtt Lo */ +#define AGNX_TXM_TBTTHI 0x0024 /* tbtt Hi */ +#define AGNX_TXM_BEAINTER 0x0028 /* Beacon Interval */ +#define AGNX_TXM_NAV 0x0030 /* NAV */ +#define AGNX_TXM_CFPMDV 0x0034 /* CFP MDV */ +#define AGNX_TXM_CFPERCNT 0x0038 /* CFP period count */ +#define AGNX_TXM_PROBDELAY 0x003c /* probe delay */ +#define AGNX_TXM_LISINTERCNT 0x0040 /* listen interval count */ +#define AGNX_TXM_DTIMPERICNT 0x004c /* DTIM period count */ + +#define AGNX_TXM_BEACON_CTL 0x005c /* beacon control */ + +#define AGNX_TXM_SCHEMPCNT 0x007c /* schedule empty count */ +#define AGNX_TXM_MAXTIMOUT 0x0084 /* max timeout exceed count */ +#define AGNX_TXM_MAXCFPTIM 0x0088 /* max CF poll timeout count */ +#define AGNX_TXM_MAXRXTIME 0x008c /* max RX timeout count */ +#define AGNX_TXM_MAXACKTIM 0x0090 /* max ACK timeout count */ +#define AGNX_TXM_DIF01 0x00a0 /* DIF 0-1 */ +#define AGNX_TXM_DIF23 0x00a4 /* DIF 2-3 */ +#define AGNX_TXM_DIF45 0x00a8 /* DIF 4-5 */ +#define AGNX_TXM_DIF67 0x00ac /* DIF 6-7 */ +#define AGNX_TXM_SIFSPIFS 0x00b0 /* SIFS/PIFS */ +#define AGNX_TXM_TIFSEIFS 0x00b4 /* TIFS/EIFS */ +#define AGNX_TXM_MAXCCACNTSLOT 0x00b8 /* max CCA count slot */ +#define AGNX_TXM_SLOTLIMIT 0x00bc /* slot limit/1 msec limit */ +#define AGNX_TXM_CFPOLLRXTIM 0x00f0 /* CF poll RX timeout count */ +#define AGNX_TXM_CFACKT11B 0x00f4 /* CF ack timeout limit for 11b */ +#define AGNX_TXM_CW0 0x0100 /* CW 0 */ +#define AGNX_TXM_SLBEALIM0 0x0108 /* short/long beacon limit 0 */ +#define AGNX_TXM_CW1 0x0120 /* CW 1 */ +#define AGNX_TXM_SLBEALIM1 0x0128 /* short/long beacon limit 1 */ +#define AGNX_TXM_CW2 0x0140 /* CW 2 */ +#define AGNX_TXM_SLBEALIM2 0x0148 /* short/long beacon limit 2 */ +#define AGNX_TXM_CW3 0x0160 /* CW 3 */ +#define AGNX_TXM_SLBEALIM3 0x0168 /* short/long beacon limit 3 */ +#define AGNX_TXM_CW4 0x0180 /* CW 4 */ +#define AGNX_TXM_SLBEALIM4 0x0188 /* short/long beacon limit 4 */ +#define AGNX_TXM_CW5 0x01a0 /* CW 5 */ +#define AGNX_TXM_SLBEALIM5 0x01a8 /* short/long beacon limit 5 */ +#define AGNX_TXM_CW6 0x01c0 /* CW 6 */ +#define AGNX_TXM_SLBEALIM6 0x01c8 /* short/long beacon limit 6 */ +#define AGNX_TXM_CW7 0x01e0 /* CW 7 */ +#define AGNX_TXM_SLBEALIM7 0x01e8 /* short/long beacon limit 7 */ +#define AGNX_TXM_BEACONTEMP 0x1000 /* beacon template */ +#define AGNX_TXM_STAPOWTEMP 0x1a00 /* Station Power Template */ + +/* Receive Management Control Registers */ +#define AGNX_RXM_BASE 0x2000 +#define AGNX_RXM_REQRATE 0x2000 /* requested rate */ +#define AGNX_RXM_MACHI 0x2004 /* first 4 bytes of mac address */ +#define AGNX_RXM_MACLO 0x2008 /* last 2 bytes of mac address */ +#define AGNX_RXM_BSSIDHI 0x200c /* bssid hi */ +#define AGNX_RXM_BSSIDLO 0x2010 /* bssid lo */ +#define AGNX_RXM_HASH_CMD_FLAG 0x2014 /* Flags for the RX Hash Command Default:0 */ +#define AGNX_RXM_HASH_CMD_HIGH 0x2018 /* The High half of the Hash Command */ +#define AGNX_RXM_HASH_CMD_LOW 0x201c /* The Low half of the Hash Command */ +#define AGNX_RXM_ROUTAB 0x2020 /* routing table */ +#define ROUTAB_SUBTYPE_SHIFT 24 +#define ROUTAB_TYPE_SHIFT 28 +#define ROUTAB_STATUS_SHIFT 30 +#define ROUTAB_RW_SHIFT 31 +#define ROUTAB_ROUTE_DROP 0xf00000 /* Drop */ +#define ROUTAB_ROUTE_CPU 0x400000 /* CPU */ +#define ROUTAB_ROUTE_ENCRY 0x500800 /* Encryption */ +#define ROUTAB_ROUTE_RFP 0x800000 /* RFP */ + +#define ROUTAB_TYPE_MANAG 0x0 /* Management */ +#define ROUTAB_TYPE_CTL 0x1 /* Control */ +#define ROUTAB_TYPE_DATA 0x2 /* Data */ + +#define ROUTAB_SUBTYPE_DATA 0x0 +#define ROUTAB_SUBTYPE_DATAACK 0x1 +#define ROUTAB_SUBTYPE_DATAPOLL 0x2 +#define ROUTAB_SUBTYPE_DATAPOLLACK 0x3 +#define ROUTAB_SUBTYPE_NULL 0x4 /* NULL */ +#define ROUTAB_SUBTYPE_NULLACK 0x5 +#define ROUTAB_SUBTYPE_NULLPOLL 0x6 +#define ROUTAB_SUBTYPE_NULLPOLLACK 0x7 +#define ROUTAB_SUBTYPE_QOSDATA 0x8 /* QOS DATA */ +#define ROUTAB_SUBTYPE_QOSDATAACK 0x9 +#define ROUTAB_SUBTYPE_QOSDATAPOLL 0xa +#define ROUTAB_SUBTYPE_QOSDATAACKPOLL 0xb +#define ROUTAB_SUBTYPE_QOSNULL 0xc +#define ROUTAB_SUBTYPE_QOSNULLACK 0xd +#define ROUTAB_SUBTYPE_QOSNULLPOLL 0xe +#define ROUTAB_SUBTYPE_QOSNULLPOLLACK 0xf +#define AGNX_RXM_DELAY11 0x2024 /* delay 11(AB) */ +#define AGNX_RXM_SOF_CNT 0x2028 /* SOF Count */ +#define AGNX_RXM_FRAG_CNT 0x202c /* Fragment Count*/ +#define AGNX_RXM_FCS_CNT 0x2030 /* FCS Count */ +#define AGNX_RXM_BSSID_MISS_CNT 0x2034 /* BSSID Miss Count */ +#define AGNX_RXM_PDU_ERR_CNT 0x2038 /* PDU Error Count */ +#define AGNX_RXM_DEST_MISS_CNT 0x203C /* Destination Miss Count */ +#define AGNX_RXM_DROP_CNT 0x2040 /* Drop Count */ +#define AGNX_RXM_ABORT_CNT 0x2044 /* Abort Count */ +#define AGNX_RXM_RELAY_CNT 0x2048 /* Relay Count */ +#define AGNX_RXM_HASH_MISS_CNT 0x204c /* Hash Miss Count */ +#define AGNX_RXM_SA_HI 0x2050 /* Address of received packet Hi */ +#define AGNX_RXM_SA_LO 0x2054 /* Address of received packet Lo */ +#define AGNX_RXM_HASH_DUMP_LST 0x2100 /* Contains Hash Data */ +#define AGNX_RXM_HASH_DUMP_MST 0x2104 /* Contains Hash Data */ +#define AGNX_RXM_HASH_DUMP_DATA 0x2108 /* The Station ID to dump */ + + +/* Encryption Managment */ +#define AGNX_ENCRY_BASE 0x2400 +#define AGNX_ENCRY_WEPKEY0 0x2440 /* wep key #0 */ +#define AGNX_ENCRY_WEPKEY1 0x2444 /* wep key #1 */ +#define AGNX_ENCRY_WEPKEY2 0x2448 /* wep key #2 */ +#define AGNX_ENCRY_WEPKEY3 0x244c /* wep key #3 */ +#define AGNX_ENCRY_CCMRECTL 0x2460 /* ccm replay control */ + + +/* Band Management Registers */ +#define AGNX_BM_BASE 0x2c00 +#define AGNX_BM_BMCTL 0x2c00 /* band management control */ +#define AGNX_BM_TXWADDR 0x2c18 /* tx workqueue address start */ +#define AGNX_BM_TXTOPEER 0x2c24 /* transmit to peers */ +#define AGNX_BM_FPLHP 0x2c2c /* free pool list head pointer */ +#define AGNX_BM_FPLTP 0x2c30 /* free pool list tail pointer */ +#define AGNX_BM_FPCNT 0x2c34 /* free pool count */ +#define AGNX_BM_CIPDUWCNT 0x2c38 /* card interface pdu workqueue count */ +#define AGNX_BM_SPPDUWCNT 0x2c3c /* sp pdu workqueue count */ +#define AGNX_BM_RFPPDUWCNT 0x2c40 /* rfp pdu workqueue count */ +#define AGNX_BM_RHPPDUWCNT 0x2c44 /* rhp pdu workqueue count */ +#define AGNX_BM_CIWQCTL 0x2c48 /* Card Interface WorkQueue Control */ +#define AGNX_BM_CPUTXWCTL 0x2c50 /* cpu tx workqueue control */ +#define AGNX_BM_CPURXWCTL 0x2c58 /* cpu rx workqueue control */ +#define AGNX_BM_CPULWCTL 0x2c60 /* cpu low workqueue control */ +#define AGNX_BM_CPUHWCTL 0x2c68 /* cpu high workqueue control */ +#define AGNX_BM_SPTXWCTL 0x2c70 /* sp tx workqueue control */ +#define AGNX_BM_SPRXWCTL 0x2c78 /* sp rx workqueue control */ +#define AGNX_BM_RFPWCTL 0x2c80 /* RFP workqueue control */ +#define AGNX_BM_MTSM 0x2c90 /* Multicast Transmit Station Mask */ + +/* Card Interface Registers (32bits) */ +#define AGNX_CIR_BASE 0x3000 +#define AGNX_CIR_BLKCTL 0x3000 /* block control*/ +#define AGNX_STAT_TX 0x1 +#define AGNX_STAT_RX 0x2 +#define AGNX_STAT_X 0x4 +/* Below two interrupt flags will be set by our but not CPU or the card */ +#define AGNX_STAT_TXD 0x10 +#define AGNX_STAT_TXM 0x20 + +#define AGNX_CIR_ADDRWIN 0x3004 /* Addressable Windows*/ +#define AGNX_CIR_ENDIAN 0x3008 /* card endianness */ +#define AGNX_CIR_SERIALITF 0x3020 /* serial interface */ +#define AGNX_CIR_RXCFG 0x3040 /* receive config */ +#define ENABLE_RX_INTERRUPT 0x20 +#define RX_CACHE_LINE 0x8 +/* the RX fragment length */ +#define FRAG_LEN_256 0x0 /* 256B */ +#define FRAG_LEN_512 0x1 +#define FRAG_LEN_1024 0x2 +#define FRAG_LEN_2048 0x3 +#define FRAG_BE 0x10 +#define AGNX_CIR_RXCTL 0x3050 /* receive control */ +/* memory address, chipside */ +#define AGNX_CIR_RXCMSTART 0x3054 /* receive client memory start */ +#define AGNX_CIR_RXCMEND 0x3058 /* receive client memory end */ +/* memory address, pci */ +#define AGNX_CIR_RXHOSTADDR 0x3060 /* receive hostside address */ +/* memory address, chipside */ +#define AGNX_CIR_RXCLIADDR 0x3064 /* receive clientside address */ +#define AGNX_CIR_RXDMACTL 0x3068 /* receive dma control */ +#define AGNX_CIR_TXCFG 0x3080 /* transmit config */ +#define AGNX_CIR_TXMCTL 0x3090 /* Transmit Management Control */ +#define ENABLE_TX_INTERRUPT 0x20 +#define TX_CACHE_LINE 0x8 +#define AGNX_CIR_TXMSTART 0x3094 /* Transmit Management Start */ +#define AGNX_CIR_TXMEND 0x3098 /* Transmit Management End */ +#define AGNX_CIR_TXDCTL 0x30a0 /* transmit data control */ +/* memeory address, chipset */ +#define AGNX_CIR_TXDSTART 0x30a4 /* transmit data start */ +#define AGNX_CIR_TXDEND 0x30a8 /* transmit data end */ +#define AGNX_CIR_TXMHADDR 0x30b0 /* Transmit Management Hostside Address */ +#define AGNX_CIR_TXMCADDR 0x30b4 /* Transmit Management Clientside Address */ +#define AGNX_CIR_TXDMACTL 0x30b8 /* transmit dma control */ + + +/* Power Managment Unit */ +#define AGNX_PM_BASE 0x3c00 +#define AGNX_PM_PMCTL 0x3c00 /* PM Control*/ +#define AGNX_PM_MACMSW 0x3c08 /* MAC Manual Slow Work Enable */ +#define AGNX_PM_RFCTL 0x3c0c /* RF Control */ +#define AGNX_PM_PHYMW 0x3c14 /* Phy Mannal Work */ +#define AGNX_PM_SOFTRST 0x3c18 /* PMU Soft Reset */ +#define AGNX_PM_PLLCTL 0x3c1c /* PMU PLL control*/ +#define AGNX_PM_TESTPHY 0x3c24 /* PMU Test Phy */ + + +/* Interrupt Control interface */ +#define AGNX_INT_BASE 0x4000 +#define AGNX_INT_STAT 0x4000 /* interrupt status */ +#define AGNX_INT_MASK 0x400c /* interrupt mask */ +/* FIXME */ +#define IRQ_TX_BEACON 0x1 /* TX Beacon */ +#define IRQ_TX_RETRY 0x8 /* TX Retry Interrupt */ +#define IRQ_TX_ACTIVITY 0x10 /* TX Activity */ +#define IRQ_RX_ACTIVITY 0x20 /* RX Activity */ +/* FIXME I guess that instead RX a none exist staion's packet or + the station hasn't been init */ +#define IRQ_RX_X 0x40 +#define IRQ_RX_Y 0x80 /* RX ? */ +#define IRQ_RX_HASHHIT 0x100 /* RX Hash Hit */ +#define IRQ_RX_FRAME 0x200 /* RX Frame */ +#define IRQ_ERR_INT 0x400 /* Error Interrupt */ +#define IRQ_TX_QUE_FULL 0x800 /* TX Workqueue Full */ +#define IRQ_BANDMAN_ERR 0x10000 /* Bandwidth Management Error */ +#define IRQ_TX_DISABLE 0x20000 /* TX Disable */ +#define IRQ_RX_IVASESKEY 0x80000 /* RX Invalid Session Key */ +#define IRQ_RX_KEYIDMIS 0x100000 /* RX key ID Mismatch */ +#define IRQ_REP_THHIT 0x200000 /* Replay Threshold Hit */ +#define IRQ_TIMER1 0x4000000 /* Timer1 */ +#define IRQ_TIMER_CNT 0x10000000 /* Timer Count */ +#define IRQ_PHY_FASTINT 0x20000000 /* Phy Fast Interrupt */ +#define IRQ_PHY_SLOWINT 0x40000000 /* Phy Slow Interrupt */ +#define IRQ_OTHER 0x80000000 /* Unknow interrupt */ +#define AGNX_IRQ_ALL 0xffffffff + +/* System Interface */ +#define AGNX_SYSITF_BASE 0x4400 +#define AGNX_SYSITF_SYSMODE 0x4400 /* system mode */ +#define AGNX_SYSITF_GPIOIN 0x4410 /* GPIO In */ +/* PIN lines for leds? */ +#define AGNX_SYSITF_GPIOUT 0x4414 /* GPIO Out */ + +/* Timer Control */ +#define AGNX_TIMCTL_TIMER1 0x4800 /* Timer 1 */ +#define AGNX_TIMCTL_TIM1CTL 0x4808 /* Timer 1 Control */ + + +/* Antenna Calibration Interface */ +#define AGNX_ACI_BASE 0x5000 +#define AGNX_ACI_MODE 0x5000 /* Mode */ +#define AGNX_ACI_MEASURE 0x5004 /* Measure */ +#define AGNX_ACI_SELCHAIN 0x5008 /* Select Chain */ +#define AGNX_ACI_LEN 0x500c /* Length */ +#define AGNX_ACI_TIMER1 0x5018 /* Timer 1 */ +#define AGNX_ACI_TIMER2 0x501c /* Timer 2 */ +#define AGNX_ACI_OFFSET 0x5020 /* Offset */ +#define AGNX_ACI_STATUS 0x5030 /* Status */ +#define CALI_IDLE 0x0 +#define CALI_DONE 0x1 +#define CALI_BUSY 0x2 +#define CALI_ERR 0x3 +#define AGNX_ACI_AICCHA0OVE 0x5034 /* AIC Channel 0 Override */ +#define AGNX_ACI_AICCHA1OVE 0x5038 /* AIC Channel 1 Override */ + +/* Gain Control Registers */ +#define AGNX_GCR_BASE 0x9000 +/* threshold of primary antenna */ +#define AGNX_GCR_THD0A 0x9000 /* threshold? D0 A */ +/* low threshold of primary antenna */ +#define AGNX_GCR_THD0AL 0x9004 /* threshold? D0 A low */ +/* threshold of secondary antenna */ +#define AGNX_GCR_THD0B 0x9008 /* threshold? D0_B */ +#define AGNX_GCR_DUNSAT 0x900c /* d unsaturated */ +#define AGNX_GCR_DSAT 0x9010 /* d saturated */ +#define AGNX_GCR_DFIRCAL 0x9014 /* D Fir/Cal */ +#define AGNX_GCR_DGCTL11A 0x9018 /* d gain control 11a */ +#define AGNX_GCR_DGCTL11B 0x901c /* d gain control 11b */ +/* strength of gain */ +#define AGNX_GCR_GAININIT 0x9020 /* gain initialization */ +#define AGNX_GCR_THNOSIG 0x9024 /* threhold no signal */ +#define AGNX_GCR_COARSTEP 0x9028 /* coarse stepping */ +#define AGNX_GCR_SIFST11A 0x902c /* sifx time 11a */ +#define AGNX_GCR_SIFST11B 0x9030 /* sifx time 11b */ +#define AGNX_GCR_CWDETEC 0x9034 /* cw detection */ +#define AGNX_GCR_0X38 0x9038 /* ???? */ +#define AGNX_GCR_BOACT 0x903c /* BO Active */ +#define AGNX_GCR_BOINACT 0x9040 /* BO Inactive */ +#define AGNX_GCR_BODYNA 0x9044 /* BO dynamic */ +/* 802.11 mode(a,b,g) */ +#define AGNX_GCR_DISCOVMOD 0x9048 /* discovery mode */ +#define AGNX_GCR_NLISTANT 0x904c /* number of listening antenna */ +#define AGNX_GCR_NACTIANT 0x9050 /* number of active antenna */ +#define AGNX_GCR_NMEASANT 0x9054 /* number of measuring antenna */ +#define AGNX_GCR_NCAPTANT 0x9058 /* number of capture antenna */ +#define AGNX_GCR_THCAP11A 0x905c /* threshold capture 11a */ +#define AGNX_GCR_THCAP11B 0x9060 /* threshold capture 11b */ +#define AGNX_GCR_THCAPRX11A 0x9064 /* threshold capture rx 11a */ +#define AGNX_GCR_THCAPRX11B 0x9068 /* threshold capture rx 11b */ +#define AGNX_GCR_THLEVDRO 0x906c /* threshold level drop */ +#define AGNX_GCR_GAINSET0 0x9070 /* Gainset 0 */ +#define AGNX_GCR_GAINSET1 0x9074 /* Gainset 1 */ +#define AGNX_GCR_GAINSET2 0x9078 /* Gainset 2 */ +#define AGNX_GCR_MAXRXTIME11A 0x907c /* maximum rx time 11a */ +#define AGNX_GCR_MAXRXTIME11B 0x9080 /* maximum rx time 11b */ +#define AGNX_GCR_CORRTIME 0x9084 /* correction time */ +/* reset the subsystem, 0 = disable, 1 = enable */ +#define AGNX_GCR_RSTGCTL 0x9088 /* reset gain control */ +/* channel receiving */ +#define AGNX_GCR_RXCHANEL 0x908c /* receive channel */ +#define AGNX_GCR_NOISE0 0x9090 /* Noise 0 */ +#define AGNX_GCR_NOISE1 0x9094 /* Noise 1 */ +#define AGNX_GCR_NOISE2 0x9098 /* Noise 2 */ +#define AGNX_GCR_SIGHTH 0x909c /* Signal High Threshold */ +#define AGNX_GCR_SIGLTH 0x90a0 /* Signal Low Threshold */ +#define AGNX_GCR_CORRDROP 0x90a4 /* correction drop */ +/* threshold of tertiay antenna */ +#define AGNX_GCR_THCD 0x90a8 /* threshold? CD */ +#define AGNX_GCR_THCS 0x90ac /* threshold? CS */ +#define AGNX_GCR_MAXPOWDIFF 0x90b8 /* maximum power difference */ +#define AGNX_GCR_TRACNT4 0x90ec /* Transition Count 4 */ +#define AGNX_GCR_TRACNT5 0x90f0 /* transition count 5 */ +#define AGNX_GCR_TRACNT6 0x90f4 /* transition count 6 */ +#define AGNX_GCR_TRACNT7 0x90f8 /* transition coutn 7 */ +#define AGNX_GCR_TESTBUS 0x911c /* test bus */ +#define AGNX_GCR_CHAINNUM 0x9120 /* Number of Chains */ +#define AGNX_GCR_ANTCFG 0x9124 /* Antenna Config */ +#define AGNX_GCR_THJUMP 0x912c /* threhold jump */ +#define AGNX_GCR_THPOWER 0x9130 /* threshold power */ +#define AGNX_GCR_THPOWCLIP 0x9134 /* threshold power clip*/ +#define AGNX_GCR_FORCECTLCLK 0x9138 /* Force Gain Control Clock */ +#define AGNX_GCR_GAINSETWRITE 0x913c /* Gainset Write */ +#define AGNX_GCR_THD0BTFEST 0x9140 /* threshold d0 b tf estimate */ +#define AGNX_GCR_THRX11BPOWMIN 0x9144 /* threshold rx 11b power minimum */ +#define AGNX_GCR_0X14c 0x914c /* ?? */ +#define AGNX_GCR_0X150 0x9150 /* ?? */ +#define AGNX_GCR_RXOVERIDE 0x9194 /* recieve override */ +#define AGNX_GCR_WATCHDOG 0x91b0 /* watchdog timeout */ + + +/* Spi Interface */ +#define AGNX_SPI_BASE 0xdc00 +#define AGNX_SPI_CFG 0xdc00 /* spi configuration */ +/* Only accept 16 bits */ +#define AGNX_SPI_WMSW 0xdc04 /* write most significant word */ +/* Only accept 16 bits */ +#define AGNX_SPI_WLSW 0xdc08 /* write least significant word */ +#define AGNX_SPI_CTL 0xdc0c /* spi control */ +#define AGNX_SPI_RMSW 0xdc10 /* read most significant word */ +#define AGNX_SPI_RLSW 0xdc14 /* read least significant word */ +/* SPI Control Mask */ +#define SPI_READ_CTL 0x4000 /* read control */ +#define SPI_BUSY_CTL 0x8000 /* busy control */ +/* RF and synth chips in spi */ +#define RF_CHIP0 0x400 +#define RF_CHIP1 0x800 +#define RF_CHIP2 0x1000 +#define SYNTH_CHIP 0x2000 + +/* Unknown register */ +#define AGNX_UNKNOWN_BASE 0x7800 + +/* FIXME MonitorGain */ +#define AGNX_MONGCR_BASE 0x12000 + +/* Gain Table */ +#define AGNX_GAIN_TABLE 0x12400 + +/* The initial FIR coefficient table */ +#define AGNX_FIR_BASE 0x19804 + +#define AGNX_ENGINE_LOOKUP_TBL 0x800 + +/* eeprom commands */ +#define EEPROM_CMD_NULL 0x0 /* NULL */ +#define EEPROM_CMD_WRITE 0x2 /* write */ +#define EEPROM_CMD_READ 0x3 /* read */ +#define EEPROM_CMD_STATUSREAD 0x5 /* status register read */ +#define EEPROM_CMD_WRITEENABLE 0x6 /* write enable */ +#define EEPROM_CMD_CONFIGURE 0x7 /* configure */ + +#define EEPROM_DATAFORCOFIGURE 0x6 /* ??? */ + +/* eeprom address */ +#define EEPROM_ADDR_SUBVID 0x0 /* Sub Vendor ID */ +#define EEPROM_ADDR_SUBSID 0x2 /* Sub System ID */ +#define EEPROM_ADDR_MACADDR 0x146 /* MAC Address */ +#define EEPROM_ADDR_LOTYPE 0x14f /* LO type */ + +struct agnx_eeprom { + u8 data; /* date */ + u16 address; /* address in EEPROM */ + u8 cmd; /* command, unknown, status */ +} __attribute__((__packed__)); + +#define AGNX_EEPROM_COMMAND_SHIFT 5 +#define AGNX_EEPROM_COMMAND_STAT 0x01 + +void disable_receiver(struct agnx_priv *priv); +void enable_receiver(struct agnx_priv *priv); +u8 read_from_eeprom(struct agnx_priv *priv, u16 address); +void agnx_hw_init(struct agnx_priv *priv); +int agnx_hw_reset(struct agnx_priv *priv); +int agnx_set_ssid(struct agnx_priv *priv, u8 *ssid, size_t ssid_len); +void agnx_set_bssid(struct agnx_priv *priv, u8 *bssid); +void enable_power_saving(struct agnx_priv *priv); +void disable_power_saving(struct agnx_priv *priv); +void calibrate_antenna_period(unsigned long data); + +#endif /* AGNX_PHY_H_ */ diff --git a/drivers/staging/agnx/rf.c b/drivers/staging/agnx/rf.c new file mode 100644 index 00000000000..8294b6e2eb9 --- /dev/null +++ b/drivers/staging/agnx/rf.c @@ -0,0 +1,894 @@ +/** + * Airgo MIMO wireless driver + * + * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> + + * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer + * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include "agnx.h" +#include "debug.h" +#include "phy.h" +#include "table.h" + +/* FIXME! */ +static inline void spi_write(void __iomem *region, u32 chip_ids, u32 sw, + u16 size, u32 control) +{ + u32 reg; + u32 lsw = sw & 0xffff; /* lower 16 bits of sw*/ + u32 msw = sw >> 16; /* high 16 bits of sw */ + + /* FIXME Write Most Significant Word of the 32bit data to MSW */ + /* FIXME And Least Significant Word to LSW */ + iowrite32((lsw), region + AGNX_SPI_WLSW); + iowrite32((msw), region + AGNX_SPI_WMSW); + reg = chip_ids | size | control; + /* Write chip id(s), write size and busy control to Control Register */ + iowrite32((reg), region + AGNX_SPI_CTL); + /* Wait for Busy control to clear */ + spi_delay(); +} + +/* + * Write to SPI Synth register + */ +static inline void spi_sy_write(void __iomem *region, u32 chip_ids, u32 sw) +{ + /* FIXME the size 0x15 is a magic value*/ + spi_write(region, chip_ids, sw, 0x15, SPI_BUSY_CTL); +} + +/* + * Write to SPI RF register + */ +static inline void spi_rf_write(void __iomem *region, u32 chip_ids, u32 sw) +{ + /* FIXME the size 0xd is a magic value*/ + spi_write(region, chip_ids, sw, 0xd, SPI_BUSY_CTL); +} /* spi_rf_write */ + +/* + * Write to SPI with Read Control bit set + */ +inline void spi_rc_write(void __iomem *region, u32 chip_ids, u32 sw) +{ + /* FIXME the size 0xe5 is a magic value */ + spi_write(region, chip_ids, sw, 0xe5, SPI_BUSY_CTL|SPI_READ_CTL); +} + +/* Get the active chains's count */ +static int get_active_chains(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int num = 0; + u32 reg; + AGNX_TRACE; + + spi_rc_write(ctl, RF_CHIP0, 0x21); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + if (reg == 1) + num++; + + spi_rc_write(ctl, RF_CHIP1, 0x21); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + if (reg == 1) + num++; + + spi_rc_write(ctl, RF_CHIP2, 0x21); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + if (reg == 1) + num++; + + spi_rc_write(ctl, RF_CHIP0, 0x26); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + if (0x33 != reg) + printk(KERN_WARNING PFX "Unmatched rf chips result\n"); + + return num; +} /* get_active_chains */ + +void rf_chips_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + int num; + AGNX_TRACE; + + if (priv->revid == 1) { + reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); + reg |= 0x8; + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); + } + + /* Set SPI clock speed to 200NS */ + reg = agnx_read32(ctl, AGNX_SPI_CFG); + reg &= ~0xF; + reg |= 0x3; + agnx_write32(ctl, AGNX_SPI_CFG, reg); + + /* Set SPI clock speed to 50NS */ + reg = agnx_read32(ctl, AGNX_SPI_CFG); + reg &= ~0xF; + reg |= 0x1; + agnx_write32(ctl, AGNX_SPI_CFG, reg); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1101); + + num = get_active_chains(priv); + printk(KERN_INFO PFX "Active chains are %d\n", num); + + reg = agnx_read32(ctl, AGNX_SPI_CFG); + reg &= ~0xF; + agnx_write32(ctl, AGNX_SPI_CFG, reg); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1908); +} /* rf_chips_init */ + + +static u32 channel_tbl[15][9] = { + {0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {1, 0x00, 0x00, 0x624, 0x00, 0x1a4, 0x28, 0x00, 0x1e}, + {2, 0x00, 0x00, 0x615, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, + {3, 0x00, 0x00, 0x61a, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, + {4, 0x00, 0x00, 0x61f, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, + {5, 0x00, 0x00, 0x624, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, + {6, 0x00, 0x00, 0x61f, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, + {7, 0x00, 0x00, 0x624, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, + {8, 0x00, 0x00, 0x629, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, + {9, 0x00, 0x00, 0x624, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, + {10, 0x00, 0x00, 0x629, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, + {11, 0x00, 0x00, 0x62e, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, + {12, 0x00, 0x00, 0x633, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, + {13, 0x00, 0x00, 0x628, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, + {14, 0x00, 0x00, 0x644, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, +}; + + +static inline void +channel_tbl_write(struct agnx_priv *priv, unsigned int channel, unsigned int reg_num) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + reg = channel_tbl[channel][reg_num]; + reg <<= 4; + reg |= reg_num; + spi_sy_write(ctl, SYNTH_CHIP, reg); +} + +static void synth_freq_set(struct agnx_priv *priv, unsigned int channel) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); + + /* Set the Clock bits to 50NS */ + reg = agnx_read32(ctl, AGNX_SPI_CFG); + reg &= ~0xF; + reg |= 0x1; + agnx_write32(ctl, AGNX_SPI_CFG, reg); + + /* Write 0x00c0 to LSW and 0x3 to MSW of Synth Chip */ + spi_sy_write(ctl, SYNTH_CHIP, 0x300c0); + + spi_sy_write(ctl, SYNTH_CHIP, 0x32); + + /* # Write to Register 1 on the Synth Chip */ + channel_tbl_write(priv, channel, 1); + /* # Write to Register 3 on the Synth Chip */ + channel_tbl_write(priv, channel, 3); + /* # Write to Register 6 on the Synth Chip */ + channel_tbl_write(priv, channel, 6); + /* # Write to Register 5 on the Synth Chip */ + channel_tbl_write(priv, channel, 5); + /* # Write to register 8 on the Synth Chip */ + channel_tbl_write(priv, channel, 8); + + /* FIXME Clear the clock bits */ + reg = agnx_read32(ctl, AGNX_SPI_CFG); + reg &= ~0xf; + agnx_write32(ctl, AGNX_SPI_CFG, reg); +} /* synth_chip_init */ + + +static void antenna_init(struct agnx_priv *priv, int num_antenna) +{ + void __iomem *ctl = priv->ctl; + + switch (num_antenna) { + case 1: + agnx_write32(ctl, AGNX_GCR_NLISTANT, 1); + agnx_write32(ctl, AGNX_GCR_NMEASANT, 1); + agnx_write32(ctl, AGNX_GCR_NACTIANT, 1); + agnx_write32(ctl, AGNX_GCR_NCAPTANT, 1); + + agnx_write32(ctl, AGNX_GCR_ANTCFG, 7); + agnx_write32(ctl, AGNX_GCR_BOACT, 34); + agnx_write32(ctl, AGNX_GCR_BOINACT, 34); + agnx_write32(ctl, AGNX_GCR_BODYNA, 30); + + agnx_write32(ctl, AGNX_GCR_THD0A, 125); + agnx_write32(ctl, AGNX_GCR_THD0AL, 100); + agnx_write32(ctl, AGNX_GCR_THD0B, 90); + + agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 80); + agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); + agnx_write32(ctl, AGNX_GCR_SIGLTH, 16); + break; + case 2: + agnx_write32(ctl, AGNX_GCR_NLISTANT, 2); + agnx_write32(ctl, AGNX_GCR_NMEASANT, 2); + agnx_write32(ctl, AGNX_GCR_NACTIANT, 2); + agnx_write32(ctl, AGNX_GCR_NCAPTANT, 2); + agnx_write32(ctl, AGNX_GCR_ANTCFG, 15); + agnx_write32(ctl, AGNX_GCR_BOACT, 36); + agnx_write32(ctl, AGNX_GCR_BOINACT, 36); + agnx_write32(ctl, AGNX_GCR_BODYNA, 32); + agnx_write32(ctl, AGNX_GCR_THD0A, 120); + agnx_write32(ctl, AGNX_GCR_THD0AL, 100); + agnx_write32(ctl, AGNX_GCR_THD0B, 80); + agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 70); + agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); + agnx_write32(ctl, AGNX_GCR_SIGLTH, 32); + break; + case 3: + agnx_write32(ctl, AGNX_GCR_NLISTANT, 3); + agnx_write32(ctl, AGNX_GCR_NMEASANT, 3); + agnx_write32(ctl, AGNX_GCR_NACTIANT, 3); + agnx_write32(ctl, AGNX_GCR_NCAPTANT, 3); + agnx_write32(ctl, AGNX_GCR_ANTCFG, 31); + agnx_write32(ctl, AGNX_GCR_BOACT, 36); + agnx_write32(ctl, AGNX_GCR_BOINACT, 36); + agnx_write32(ctl, AGNX_GCR_BODYNA, 32); + agnx_write32(ctl, AGNX_GCR_THD0A, 100); + agnx_write32(ctl, AGNX_GCR_THD0AL, 100); + agnx_write32(ctl, AGNX_GCR_THD0B, 70); + agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 70); + agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); + agnx_write32(ctl, AGNX_GCR_SIGLTH, 48); +// agnx_write32(ctl, AGNX_GCR_SIGLTH, 16); + break; + default: + printk(KERN_WARNING PFX "Unknow antenna number\n"); + } +} /* antenna_init */ + +static void chain_update(struct agnx_priv *priv, u32 chain) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + spi_rc_write(ctl, RF_CHIP0, 0x20); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + + if (reg == 0x4) + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, reg|0x1000); + else if (reg != 0x0) + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, reg|0x1000); + else { + if (chain == 3 || chain == 6) { + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, reg|0x1000); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + } else if (chain == 2 || chain == 4) { + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, reg|0x1000); + spi_rf_write(ctl, RF_CHIP2, 0x1005); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x824); + } else if (chain == 1) { + spi_rf_write(ctl, RF_CHIP0, reg|0x1000); + spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1004); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xc36); + } + } + + spi_rc_write(ctl, RF_CHIP0, 0x22); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + + switch (reg) { + case 0: + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1005); + break; + case 1: + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); + break; + case 2: + if (chain == 6 || chain == 4) { + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1202); + spi_rf_write(ctl, RF_CHIP2, 0x1005); + } else if (chain < 3) { + spi_rf_write(ctl, RF_CHIP0, 0x1202); + spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1005); + } + break; + default: + if (chain == 3) { + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); + spi_rf_write(ctl, RF_CHIP2, 0x1201); + } else if (chain == 2) { + spi_rf_write(ctl, RF_CHIP0, 0x1203); + spi_rf_write(ctl, RF_CHIP2, 0x1200); + spi_rf_write(ctl, RF_CHIP1, 0x1201); + } else if (chain == 1) { + spi_rf_write(ctl, RF_CHIP0, 0x1203); + spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1200); + } else if (chain == 4) { + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); + spi_rf_write(ctl, RF_CHIP2, 0x1201); + } else { + spi_rf_write(ctl, RF_CHIP0, 0x1203); + spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1201); + } + } +} /* chain_update */ + +static void antenna_config(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + /* Write 0x0 to the TX Management Control Register Enable bit */ + reg = agnx_read32(ctl, AGNX_TXM_CTL); + reg &= ~0x1; + agnx_write32(ctl, AGNX_TXM_CTL, reg); + + /* FIXME */ + /* Set initial value based on number of Antennae */ + antenna_init(priv, 3); + + /* FIXME Update Power Templates for current valid Stations */ + /* sta_power_init(priv, 0);*/ + + /* FIXME the number of chains should get from eeprom*/ + chain_update(priv, AGNX_CHAINS_MAX); +} /* antenna_config */ + +void calibrate_oscillator(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); + reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); + reg |= 0x10; + agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); + + agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 1); + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 1); + + agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); + + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); + /* (Residual DC Calibration) to Calibration Mode */ + agnx_write32(ctl, AGNX_ACI_MODE, 0x2); + + spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x1004); + agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); + /* (TX LO Calibration) to Calibration Mode */ + agnx_write32(ctl, AGNX_ACI_MODE, 0x4); + + do { + u32 reg1, reg2, reg3; + /* Enable Power Saving Control */ + enable_power_saving(priv); + /* Save the following registers to restore */ + reg1 = ioread32(ctl + 0x11000); + reg2 = ioread32(ctl + 0xec50); + reg3 = ioread32(ctl + 0xec54); + wmb(); + + agnx_write32(ctl, 0x11000, 0xcfdf); + agnx_write32(ctl, 0xec50, 0x70); + /* Restore the registers */ + agnx_write32(ctl, 0x11000, reg1); + agnx_write32(ctl, 0xec50, reg2); + agnx_write32(ctl, 0xec54, reg3); + /* Disable Power Saving Control */ + disable_power_saving(priv); + } while (0); + + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0); +} /* calibrate_oscillator */ + + +static void radio_channel_set(struct agnx_priv *priv, unsigned int channel) +{ + void __iomem *ctl = priv->ctl; + unsigned int freq = priv->band.channels[channel - 1].center_freq; + u32 reg; + AGNX_TRACE; + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); + /* Set SPI Clock to 50 Ns */ + reg = agnx_read32(ctl, AGNX_SPI_CFG); + reg &= ~0xF; + reg |= 0x1; + agnx_write32(ctl, AGNX_SPI_CFG, reg); + + /* Clear the Disable Tx interrupt bit in Interrupt Mask */ +/* reg = agnx_read32(ctl, AGNX_INT_MASK); */ +/* reg &= ~IRQ_TX_DISABLE; */ +/* agnx_write32(ctl, AGNX_INT_MASK, reg); */ + + /* Band Selection */ + reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); + reg |= 0x8; + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); + + /* FIXME Set the SiLabs Chip Frequency */ + synth_freq_set(priv, channel); + + reg = agnx_read32(ctl, AGNX_PM_SOFTRST); + reg |= 0x80100030; + agnx_write32(ctl, AGNX_PM_SOFTRST, reg); + reg = agnx_read32(ctl, AGNX_PM_PLLCTL); + reg |= 0x20009; + agnx_write32(ctl, AGNX_PM_PLLCTL, reg); + + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1100); + + /* Load the MonitorGain Table */ + monitor_gain_table_init(priv); + + /* Load the TX Fir table */ + tx_fir_table_init(priv); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg |= 0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + + spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x22); + udelay(80); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + + + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xff); + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); + + reg = agnx_read32(ctl, 0xec50); + reg |= 0x4f; + agnx_write32(ctl, 0xec50, reg); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); + agnx_write32(ctl, 0x11008, 0x1); + agnx_write32(ctl, 0x1100c, 0x0); + agnx_write32(ctl, 0x11008, 0x0); + agnx_write32(ctl, 0xec50, 0xc); + + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + agnx_write32(ctl, 0x11010, 0x6e); + agnx_write32(ctl, 0x11014, 0x6c); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); + + /* Calibrate the Antenna */ + /* antenna_calibrate(priv); */ + /* Calibrate the TxLocalOscillator */ + calibrate_oscillator(priv); + + reg = agnx_read32(ctl, AGNX_PM_PMCTL); + reg &= ~0x8; + agnx_write32(ctl, AGNX_PM_PMCTL, reg); + agnx_write32(ctl, AGNX_GCR_GAININIT, 0xa); + agnx_write32(ctl, AGNX_GCR_THCD, 0x0); + + agnx_write32(ctl, 0x11018, 0xb); + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x0); + + /* Write Frequency to Gain Control Channel */ + agnx_write32(ctl, AGNX_GCR_RXCHANEL, freq); + /* Write 0x140000/Freq to 0x9c08 */ + reg = 0x140000/freq; + agnx_write32(ctl, 0x9c08, reg); + + reg = agnx_read32(ctl, AGNX_PM_SOFTRST); + reg &= ~0x80100030; + agnx_write32(ctl, AGNX_PM_SOFTRST, reg); + + reg = agnx_read32(ctl, AGNX_PM_PLLCTL); + reg &= ~0x20009; + reg |= 0x1; + agnx_write32(ctl, AGNX_PM_PLLCTL, reg); + + agnx_write32(ctl, AGNX_ACI_MODE, 0x0); + +/* FIXME According to Number of Chains: */ + +/* 1. 1: */ +/* 1. Write 0x1203 to RF Chip 0 */ +/* 2. Write 0x1200 to RF Chips 1 +2 */ +/* 2. 2: */ +/* 1. Write 0x1203 to RF Chip 0 */ +/* 2. Write 0x1200 to RF Chip 2 */ +/* 3. Write 0x1201 to RF Chip 1 */ +/* 3. 3: */ +/* 1. Write 0x1203 to RF Chip 0 */ +/* 2. Write 0x1201 to RF Chip 1 + 2 */ +/* 4. 4: */ +/* 1. Write 0x1203 to RF Chip 0 + 1 */ +/* 2. Write 0x1200 to RF Chip 2 */ + +/* 5. 6: */ + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); + spi_rf_write(ctl, RF_CHIP2, 0x1201); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1000); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + + /* FIXME Set the Disable Tx interrupt bit in Interrupt Mask + (Or 0x20000 to Interrupt Mask) */ +/* reg = agnx_read32(ctl, AGNX_INT_MASK); */ +/* reg |= IRQ_TX_DISABLE; */ +/* agnx_write32(ctl, AGNX_INT_MASK, reg); */ + + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); + + /* Configure the Antenna */ + antenna_config(priv); + + /* Write 0x0 to Discovery Mode Enable detect G, B, A packet? */ + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0); + + reg = agnx_read32(ctl, AGNX_RXM_REQRATE); + reg |= 0x80000000; + agnx_write32(ctl, AGNX_RXM_REQRATE, reg); + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); + + /* enable radio on and the power LED */ + reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); + reg &= ~0x1; + reg |= 0x2; + agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); + + reg = agnx_read32(ctl, AGNX_TXM_CTL); + reg |= 0x1; + agnx_write32(ctl, AGNX_TXM_CTL, reg); +} /* radio_channel_set */ + +static void base_band_filter_calibrate(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1700); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1001); + agnx_write32(ctl, AGNX_GCR_FORCECTLCLK, 0x0); + spi_rc_write(ctl, RF_CHIP0, 0x27); + spi_rc_write(ctl, RF_CHIP1, 0x27); + spi_rc_write(ctl, RF_CHIP2, 0x27); + agnx_write32(ctl, AGNX_GCR_FORCECTLCLK, 0x1); +} + +static void print_offset(struct agnx_priv *priv, u32 chain) +{ + void __iomem *ctl = priv->ctl; + u32 offset; + + iowrite32((chain), ctl + AGNX_ACI_SELCHAIN); + udelay(10); + offset = (ioread32(ctl + AGNX_ACI_OFFSET)); + printk(PFX "Chain is 0x%x, Offset is 0x%x\n", chain, offset); +} + +void print_offsets(struct agnx_priv *priv) +{ + print_offset(priv, 0); + print_offset(priv, 4); + print_offset(priv, 1); + print_offset(priv, 5); + print_offset(priv, 2); + print_offset(priv, 6); +} + + +struct chains { + u32 cali; /* calibrate value*/ + +#define NEED_CALIBRATE 0 +#define SUCCESS_CALIBRATE 1 + int status; +}; + +static void chain_calibrate(struct agnx_priv *priv, struct chains *chains, + unsigned int num) +{ + void __iomem *ctl = priv->ctl; + u32 calibra = chains[num].cali; + + if (num < 3) + calibra |= 0x1400; + else + calibra |= 0x1500; + + switch (num) { + case 0: + case 4: + spi_rf_write(ctl, RF_CHIP0, calibra); + break; + case 1: + case 5: + spi_rf_write(ctl, RF_CHIP1, calibra); + break; + case 2: + case 6: + spi_rf_write(ctl, RF_CHIP2, calibra); + break; + default: + BUG(); + } +} /* chain_calibrate */ + + +static void inline get_calibrete_value(struct agnx_priv *priv, struct chains *chains, + unsigned int num) +{ + void __iomem *ctl = priv->ctl; + u32 offset; + + iowrite32((num), ctl + AGNX_ACI_SELCHAIN); + /* FIXME */ + udelay(10); + offset = (ioread32(ctl + AGNX_ACI_OFFSET)); + + if (offset < 0xf) { + chains[num].status = SUCCESS_CALIBRATE; + return; + } + + if (num == 0 || num == 1 || num == 2) { + if ( 0 == chains[num].cali) + chains[num].cali = 0xff; + else + chains[num].cali--; + } else + chains[num].cali++; + + chains[num].status = NEED_CALIBRATE; +} + +static inline void calibra_delay(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + unsigned int i = 100; + + wmb(); + while (i--) { + reg = (ioread32(ctl + AGNX_ACI_STATUS)); + if (reg == 0x4000) + break; + udelay(10); + } + if (!i) + printk(PFX "calibration failed\n"); +} + +void do_calibration(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + struct chains chains[7]; + unsigned int i, j; + AGNX_TRACE; + + for (i = 0; i < 7; i++) { + if (i == 3) + continue; + + chains[i].cali = 0x7f; + chains[i].status = NEED_CALIBRATE; + } + + /* FIXME 0x300 is a magic number */ + for (j = 0; j < 0x300; j++) { + if (chains[0].status == SUCCESS_CALIBRATE && + chains[1].status == SUCCESS_CALIBRATE && + chains[2].status == SUCCESS_CALIBRATE && + chains[4].status == SUCCESS_CALIBRATE && + chains[5].status == SUCCESS_CALIBRATE && + chains[6].status == SUCCESS_CALIBRATE) + break; + + /* Attention, there is no chain 3 */ + for (i = 0; i < 7; i++) { + if (i == 3) + continue; + if (chains[i].status == NEED_CALIBRATE) + chain_calibrate(priv, chains, i); + } + /* Write 0x1 to Calibration Measure */ + iowrite32((0x1), ctl + AGNX_ACI_MEASURE); + calibra_delay(priv); + + for (i = 0; i < 7; i++) { + if (i == 3) + continue; + + get_calibrete_value(priv, chains, i); + } + } + printk(PFX "Clibrate times is %d\n", j); + print_offsets(priv); +} /* do_calibration */ + +void antenna_calibrate(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + AGNX_TRACE; + + agnx_write32(ctl, AGNX_GCR_NLISTANT, 0x3); + agnx_write32(ctl, AGNX_GCR_NMEASANT, 0x3); + agnx_write32(ctl, AGNX_GCR_NACTIANT, 0x3); + agnx_write32(ctl, AGNX_GCR_NCAPTANT, 0x3); + + agnx_write32(ctl, AGNX_GCR_ANTCFG, 0x1f); + agnx_write32(ctl, AGNX_GCR_BOACT, 0x24); + agnx_write32(ctl, AGNX_GCR_BOINACT, 0x24); + agnx_write32(ctl, AGNX_GCR_BODYNA, 0x20); + agnx_write32(ctl, AGNX_GCR_THD0A, 0x64); + agnx_write32(ctl, AGNX_GCR_THD0AL, 0x64); + agnx_write32(ctl, AGNX_GCR_THD0B, 0x46); + agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x3c); + agnx_write32(ctl, AGNX_GCR_SIGHTH, 0x64); + agnx_write32(ctl, AGNX_GCR_SIGLTH, 0x30); + + spi_rc_write(ctl, RF_CHIP0, 0x20); + /* Fixme */ + udelay(80); + /* 1. Should read 0x0 */ + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + if (0x0 != reg) + printk(KERN_WARNING PFX "Unmatched rf chips result\n"); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1000); + + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + + spi_rc_write(ctl, RF_CHIP0, 0x22); + udelay(80); + reg = agnx_read32(ctl, AGNX_SPI_RLSW); + if (0x0 != reg) + printk(KERN_WARNING PFX "Unmatched rf chips result\n"); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1005); + + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); + agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); + + reg = agnx_read32(ctl, AGNX_PM_SOFTRST); + reg |= 0x1c000032; + agnx_write32(ctl, AGNX_PM_SOFTRST, reg); + reg = agnx_read32(ctl, AGNX_PM_PLLCTL); + reg |= 0x0003f07; + agnx_write32(ctl, AGNX_PM_PLLCTL, reg); + + reg = agnx_read32(ctl, 0xec50); + reg |= 0x40; + agnx_write32(ctl, 0xec50, reg); + + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xff8); + agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); + + agnx_write32(ctl, AGNX_GCR_CHAINNUM, 0x6); + agnx_write32(ctl, 0x19874, 0x0); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1700); + + /* Calibrate the BaseBandFilter */ + base_band_filter_calibrate(priv); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1002); + + agnx_write32(ctl, AGNX_GCR_GAINSET0, 0x1d); + agnx_write32(ctl, AGNX_GCR_GAINSET1, 0x1d); + agnx_write32(ctl, AGNX_GCR_GAINSET2, 0x1d); + agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x1); + + agnx_write32(ctl, AGNX_ACI_MODE, 0x1); + agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); + + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); + + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1400); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1500); + + /* Measure Calibration */ + agnx_write32(ctl, AGNX_ACI_MEASURE, 0x1); + calibra_delay(priv); + + /* do calibration */ + do_calibration(priv); + + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); + agnx_write32(ctl, AGNX_ACI_LEN, 0xf); + + reg = agnx_read32(ctl, AGNX_GCR_GAINSET0); + reg &= 0xf; + agnx_write32(ctl, AGNX_GCR_GAINSET0, reg); + reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); + reg &= 0xf; + agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); + reg = agnx_read32(ctl, AGNX_GCR_GAINSET2); + reg &= 0xf; + agnx_write32(ctl, AGNX_GCR_GAINSET2, reg); + + agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x0); + disable_receiver(priv); +} /* antenna_calibrate */ + +void __antenna_calibrate(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + /* Calibrate the BaseBandFilter */ + /* base_band_filter_calibrate(priv); */ + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1002); + + + agnx_write32(ctl, AGNX_GCR_GAINSET0, 0x1d); + agnx_write32(ctl, AGNX_GCR_GAINSET1, 0x1d); + agnx_write32(ctl, AGNX_GCR_GAINSET2, 0x1d); + + agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x1); + + agnx_write32(ctl, AGNX_ACI_MODE, 0x1); + agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); + + + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1400); + spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1500); + /* Measure Calibration */ + agnx_write32(ctl, AGNX_ACI_MEASURE, 0x1); + calibra_delay(priv); + do_calibration(priv); + agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); + + agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); + agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); + + agnx_write32(ctl, AGNX_ACI_LEN, 0xf); + + reg = agnx_read32(ctl, AGNX_GCR_GAINSET0); + reg &= 0xf; + agnx_write32(ctl, AGNX_GCR_GAINSET0, reg); + reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); + reg &= 0xf; + agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); + reg = agnx_read32(ctl, AGNX_GCR_GAINSET2); + reg &= 0xf; + agnx_write32(ctl, AGNX_GCR_GAINSET2, reg); + + + agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x0); + + /* Write 0x3 Gain Control Discovery Mode */ + enable_receiver(priv); +} + +int agnx_set_channel(struct agnx_priv *priv, unsigned int channel) +{ + AGNX_TRACE; + + printk(KERN_ERR PFX "Channel is %d %s\n", channel, __func__); + radio_channel_set(priv, channel); + return 0; +} diff --git a/drivers/staging/agnx/sta.c b/drivers/staging/agnx/sta.c new file mode 100644 index 00000000000..d3ac675e45b --- /dev/null +++ b/drivers/staging/agnx/sta.c @@ -0,0 +1,219 @@ +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include "phy.h" +#include "sta.h" +#include "debug.h" + +void hash_read(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id) +{ + void __iomem *ctl = priv->ctl; + + reglo &= 0xFFFF; + reglo |= 0x30000000; + reglo |= 0x40000000; /* Set status busy */ + reglo |= sta_id << 16; + + iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); + iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); + iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); + + reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); + reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); + printk(PFX "RX hash cmd are : %.8x%.8x\n", reghi, reglo); +} + +void hash_write(struct agnx_priv *priv, u8 *mac_addr, u8 sta_id) +{ + void __iomem *ctl = priv->ctl; + u32 reghi, reglo; + + if (!is_valid_ether_addr(mac_addr)) + printk(KERN_WARNING PFX "Update hash table: Invalid hwaddr!\n"); + + reghi = mac_addr[0] << 24 | mac_addr[1] << 16 | mac_addr[2] << 8 | mac_addr[3]; + reglo = mac_addr[4] << 8 | mac_addr[5]; + reglo |= 0x10000000; /* Set hash commmand */ + reglo |= 0x40000000; /* Set status busy */ + reglo |= sta_id << 16; + + iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); + iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); + iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); + + reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); + if (!(reglo & 0x80000000)) + printk(KERN_WARNING PFX "Update hash table failed\n"); +} + +void hash_delete(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id) +{ + void __iomem *ctl = priv->ctl; + + reglo &= 0xFFFF; + reglo |= 0x20000000; + reglo |= 0x40000000; /* Set status busy */ + reglo |= sta_id << 16; + + iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); + iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); + iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); + reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); + + reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); + printk(PFX "RX hash cmd are : %.8x%.8x\n", reghi, reglo); + +} + +void hash_dump(struct agnx_priv *priv, u8 sta_id) +{ + void __iomem *ctl = priv->ctl; + u32 reghi, reglo; + + reglo = 0x0; /* dump command */ + reglo|= 0x40000000; /* status bit */ + iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); + iowrite32(sta_id << 16, ctl + AGNX_RXM_HASH_DUMP_DATA); + + udelay(80); + + reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); + reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); + printk(PFX "hash cmd are : %.8x%.8x\n", reghi, reglo); + reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_FLAG); + printk(PFX "hash flag is : %.8x\n", reghi); + reghi = ioread32(ctl + AGNX_RXM_HASH_DUMP_MST); + reglo = ioread32(ctl + AGNX_RXM_HASH_DUMP_LST); + printk(PFX "hash dump mst lst: %.8x%.8x\n", reghi, reglo); + reghi = ioread32(ctl + AGNX_RXM_HASH_DUMP_DATA); + printk(PFX "hash dump data: %.8x\n", reghi); +} + +void get_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx) +{ + void __iomem *ctl = priv->ctl; + memcpy_fromio(power, ctl + AGNX_TXM_STAPOWTEMP + sizeof(*power) * sta_idx, + sizeof(*power)); +} + +inline void +set_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx) +{ + void __iomem *ctl = priv->ctl; + /* FIXME 2. Write Template to offset + station number */ + memcpy_toio(ctl + AGNX_TXM_STAPOWTEMP + sizeof(*power) * sta_idx, + power, sizeof(*power)); +} + + +void get_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, + unsigned int sta_idx, unsigned int wq_idx) +{ + void __iomem *data = priv->data; + memcpy_fromio(tx_wq, data + AGNX_PDU_TX_WQ + sizeof(*tx_wq) * STA_TX_WQ_NUM * sta_idx + + sizeof(*tx_wq) * wq_idx, sizeof(*tx_wq)); + +} + +inline void set_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, + unsigned int sta_idx, unsigned int wq_idx) +{ + void __iomem *data = priv->data; + memcpy_toio(data + AGNX_PDU_TX_WQ + sizeof(*tx_wq) * STA_TX_WQ_NUM * sta_idx + + sizeof(*tx_wq) * wq_idx, tx_wq, sizeof(*tx_wq)); +} + + +void get_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx) +{ + void __iomem *data = priv->data; + + memcpy_fromio(sta, data + AGNX_PDUPOOL + sizeof(*sta) * sta_idx, + sizeof(*sta)); +} + +inline void set_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx) +{ + void __iomem *data = priv->data; + + memcpy_toio(data + AGNX_PDUPOOL + sizeof(*sta) * sta_idx, + sta, sizeof(*sta)); +} + +/* FIXME */ +void sta_power_init(struct agnx_priv *priv, unsigned int sta_idx) +{ + struct agnx_sta_power power; + u32 reg; + AGNX_TRACE; + + memset(&power, 0, sizeof(power)); + reg = agnx_set_bits(EDCF, EDCF_SHIFT, 0x1); + power.reg = cpu_to_le32(reg); + set_sta_power(priv, &power, sta_idx); + udelay(40); +} /* add_power_template */ + + +/* @num: The #number of station that is visible to the card */ +static void sta_tx_workqueue_init(struct agnx_priv *priv, unsigned int sta_idx) +{ + struct agnx_sta_tx_wq tx_wq; + u32 reg; + unsigned int i; + + memset(&tx_wq, 0, sizeof(tx_wq)); + + reg = agnx_set_bits(WORK_QUEUE_VALID, WORK_QUEUE_VALID_SHIFT, 1); + reg |= agnx_set_bits(WORK_QUEUE_ACK_TYPE, WORK_QUEUE_ACK_TYPE_SHIFT, 1); +// reg |= agnx_set_bits(WORK_QUEUE_ACK_TYPE, WORK_QUEUE_ACK_TYPE_SHIFT, 0); + tx_wq.reg2 |= cpu_to_le32(reg); + + /* Suppose all 8 traffic class are used */ + for (i = 0; i < STA_TX_WQ_NUM; i++) + set_sta_tx_wq(priv, &tx_wq, sta_idx, i); +} /* sta_tx_workqueue_init */ + + +static void sta_traffic_init(struct agnx_sta_traffic *traffic) +{ + u32 reg; + memset(traffic, 0, sizeof(*traffic)); + + reg = agnx_set_bits(NEW_PACKET, NEW_PACKET_SHIFT, 1); + reg |= agnx_set_bits(TRAFFIC_VALID, TRAFFIC_VALID_SHIFT, 1); +// reg |= agnx_set_bits(TRAFFIC_ACK_TYPE, TRAFFIC_ACK_TYPE_SHIFT, 1); + traffic->reg0 = cpu_to_le32(reg); + + /* 3. setting RX Sequence Number to 4095 */ + reg = agnx_set_bits(RX_SEQUENCE_NUM, RX_SEQUENCE_NUM_SHIFT, 4095); + traffic->reg1 = cpu_to_le32(reg); +} + + +/* @num: The #number of station that is visible to the card */ +void sta_init(struct agnx_priv *priv, unsigned int sta_idx) +{ + /* FIXME the length of sta is 256 bytes Is that + * dangerous to stack overflow? */ + struct agnx_sta sta; + u32 reg; + int i; + + memset(&sta, 0, sizeof(sta)); + /* Set valid to 1 */ + reg = agnx_set_bits(STATION_VALID, STATION_VALID_SHIFT, 1); + /* Set Enable Concatenation to 0 (?) */ + reg |= agnx_set_bits(ENABLE_CONCATENATION, ENABLE_CONCATENATION_SHIFT, 0); + /* Set Enable Decompression to 0 (?) */ + reg |= agnx_set_bits(ENABLE_DECOMPRESSION, ENABLE_DECOMPRESSION_SHIFT, 0); + sta.reg = cpu_to_le32(reg); + + /* Initialize each of the Traffic Class Structures by: */ + for (i = 0; i < 8; i++) + sta_traffic_init(sta.traffic + i); + + set_sta(priv, &sta, sta_idx); + sta_tx_workqueue_init(priv, sta_idx); +} /* sta_descriptor_init */ + + diff --git a/drivers/staging/agnx/sta.h b/drivers/staging/agnx/sta.h new file mode 100644 index 00000000000..58d0b12900d --- /dev/null +++ b/drivers/staging/agnx/sta.h @@ -0,0 +1,222 @@ +#ifndef AGNX_STA_H_ +#define AGNX_STA_H_ + +#define STA_TX_WQ_NUM 8 /* The number of TX workqueue one STA has */ + +struct agnx_hash_cmd { + __be32 cmdhi; +#define MACLO 0xFFFF0000 +#define MACLO_SHIFT 16 +#define STA_ID 0x0000FFF0 +#define STA_ID_SHIFT 4 +#define CMD 0x0000000C +#define CMD_SHIFT 2 +#define STATUS 0x00000002 +#define STATUS_SHIFT 1 +#define PASS 0x00000001 +#define PASS_SHIFT 1 + __be32 cmdlo; +}__attribute__((__packed__)); + + +/* + * Station Power Template + * FIXME Just for agn100 yet + */ +struct agnx_sta_power { + __le32 reg; +#define SIGNAL 0x000000FF /* signal */ +#define SIGNAL_SHIFT 0 +#define RATE 0x00000F00 +#define RATE_SHIFT 8 +#define TIFS 0x00001000 +#define TIFS_SHIFT 12 +#define EDCF 0x00002000 +#define EDCF_SHIFT 13 +#define CHANNEL_BOND 0x00004000 +#define CHANNEL_BOND_SHIFT 14 +#define PHY_MODE 0x00038000 +#define PHY_MODE_SHIFT 15 +#define POWER_LEVEL 0x007C0000 +#define POWER_LEVEL_SHIFT 18 +#define NUM_TRANSMITTERS 0x00800000 +#define NUM_TRANSMITTERS_SHIFT 23 +} __attribute__((__packed__)); + +/* + * TX Workqueue Descriptor + */ +struct agnx_sta_tx_wq { + __le32 reg0; +#define HEAD_POINTER_LOW 0xFF000000 /* Head pointer low */ +#define HEAD_POINTER_LOW_SHIFT 24 +#define TAIL_POINTER 0x00FFFFFF /* Tail pointer */ +#define TAIL_POINTER_SHIFT 0 + + __le32 reg3; +#define ACK_POINTER_LOW 0xFFFF0000 /* ACK pointer low */ +#define ACK_POINTER_LOW_SHIFT 16 +#define HEAD_POINTER_HIGH 0x0000FFFF /* Head pointer high */ +#define HEAD_POINTER_HIGH_SHIFT 0 + + __le32 reg1; +/* ACK timeout tail packet count */ +#define ACK_TIMOUT_TAIL_PACK_CNT 0xFFF00000 +#define ACK_TIMOUT_TAIL_PACK_CNT_SHIFT 20 +/* Head timeout tail packet count */ +#define HEAD_TIMOUT_TAIL_PACK_CNT 0x000FFF00 +#define HEAD_TIMOUT_TAIL_PACK_CNT_SHIFT 8 +#define ACK_POINTER_HIGH 0x000000FF /* ACK pointer high */ +#define ACK_POINTER_HIGH_SHIFT 0 + + __le32 reg2; +#define WORK_QUEUE_VALID 0x80000000 /* valid */ +#define WORK_QUEUE_VALID_SHIFT 31 +#define WORK_QUEUE_ACK_TYPE 0x40000000 /* ACK type */ +#define WORK_QUEUE_ACK_TYPE_SHIFT 30 +/* Head timeout window limit fragmentation count */ +#define HEAD_TIMOUT_WIN_LIM_FRAG_CNT 0x3FFF0000 +#define HEAD_TIMOUT_WIN_LIM_FRAG_CNT_SHIFT 16 +/* Head timeout window limit byte count */ +#define HEAD_TIMOUT_WIN_LIM_BYTE_CNT 0x0000FFFF +#define HEAD_TIMOUT_WIN_LIM_BYTE_CNT_SHIFT 0 +} __attribute__((__packed__)); + + +/* + * Traffic Class Structure + */ +struct agnx_sta_traffic { + __le32 reg0; +#define ACK_TIMOUT_CNT 0xFF800000 /* ACK Timeout Counts */ +#define ACK_TIMOUT_CNT_SHIFT 23 +#define TRAFFIC_ACK_TYPE 0x00600000 /* ACK Type */ +#define TRAFFIC_ACK_TYPE_SHIFT 21 +#define NEW_PACKET 0x00100000 /* New Packet */ +#define NEW_PACKET_SHIFT 20 +#define TRAFFIC_VALID 0x00080000 /* Valid */ +#define TRAFFIC_VALID_SHIFT 19 +#define RX_HDR_DESC_POINTER 0x0007FFFF /* RX Header Descripter pointer */ +#define RX_HDR_DESC_POINTER_SHIFT 0 + + __le32 reg1; +#define RX_PACKET_TIMESTAMP 0xFFFF0000 /* RX Packet Timestamp */ +#define RX_PACKET_TIMESTAMP_SHIFT 16 +#define TRAFFIC_RESERVED 0x0000E000 /* Reserved */ +#define TRAFFIC_RESERVED_SHIFT 13 +#define SV 0x00001000 /* sv */ +#define SV_SHIFT 12 +#define RX_SEQUENCE_NUM 0x00000FFF /* RX Sequence Number */ +#define RX_SEQUENCE_NUM_SHIFT 0 + + __le32 tx_replay_cnt_low; /* TX Replay Counter Low */ + + __le16 tx_replay_cnt_high; /* TX Replay Counter High */ + __le16 rx_replay_cnt_high; /* RX Replay Counter High */ + + __be32 rx_replay_cnt_low; /* RX Replay Counter Low */ +} __attribute__((__packed__)); + +/* + * Station Descriptors + */ +struct agnx_sta { + __le32 tx_session_keys[4]; /* Transmit Session Key (0-3) */ + __le32 rx_session_keys[4]; /* Receive Session Key (0-3) */ + + __le32 reg; +#define ID_1 0xC0000000 /* id 1 */ +#define ID_1_SHIFT 30 +#define ID_0 0x30000000 /* id 0 */ +#define ID_0_SHIFT 28 +#define ENABLE_CONCATENATION 0x0FF00000 /* Enable concatenation */ +#define ENABLE_CONCATENATION_SHIFT 20 +#define ENABLE_DECOMPRESSION 0x000FF000 /* Enable decompression */ +#define ENABLE_DECOMPRESSION_SHIFT 12 +#define STA_RESERVED 0x00000C00 /* Reserved */ +#define STA_RESERVED_SHIFT 10 +#define EAP 0x00000200 /* EAP */ +#define EAP_SHIFT 9 +#define ED_NULL 0x00000100 /* ED NULL */ +#define ED_NULL_SHIFT 8 +#define ENCRYPTION_POLICY 0x000000E0 /* Encryption Policy */ +#define ENCRYPTION_POLICY_SHIFT 5 +#define DEFINED_KEY_ID 0x00000018 /* Defined Key ID */ +#define DEFINED_KEY_ID_SHIFT 3 +#define FIXED_KEY 0x00000004 /* Fixed Key */ +#define FIXED_KEY_SHIFT 2 +#define KEY_VALID 0x00000002 /* Key Valid */ +#define KEY_VALID_SHIFT 1 +#define STATION_VALID 0x00000001 /* Station Valid */ +#define STATION_VALID_SHIFT 0 + + __le32 tx_aes_blks_unicast; /* TX AES Blks Unicast */ + __le32 rx_aes_blks_unicast; /* RX AES Blks Unicast */ + + __le16 aes_format_err_unicast_cnt; /* AES Format Error Unicast Counts */ + __le16 aes_replay_unicast; /* AES Replay Unicast */ + + __le16 aes_decrypt_err_unicast; /* AES Decrypt Error Unicast */ + __le16 aes_decrypt_err_default; /* AES Decrypt Error default */ + + __le16 single_retry_packets; /* Single Retry Packets */ + __le16 failed_tx_packets; /* Failed Tx Packets */ + + __le16 muti_retry_packets; /* Multiple Retry Packets */ + __le16 ack_timeouts; /* ACK Timeouts */ + + __le16 frag_tx_cnt; /* Fragment TX Counts */ + __le16 rts_brq_sent; /* RTS Brq Sent */ + + __le16 tx_packets; /* TX Packets */ + __le16 cts_back_timeout; /* CTS Back Timeout */ + + __le32 phy_stats_high; /* PHY Stats High */ + __le32 phy_stats_low; /* PHY Stats Low */ + + struct agnx_sta_traffic traffic[8]; /* Traffic Class Structure (8) */ + + __le16 traffic_class0_frag_success; /* Traffic Class 0 Fragment Success */ + __le16 traffic_class1_frag_success; /* Traffic Class 1 Fragment Success */ + __le16 traffic_class2_frag_success; /* Traffic Class 2 Fragment Success */ + __le16 traffic_class3_frag_success; /* Traffic Class 3 Fragment Success */ + __le16 traffic_class4_frag_success; /* Traffic Class 4 Fragment Success */ + __le16 traffic_class5_frag_success; /* Traffic Class 5 Fragment Success */ + __le16 traffic_class6_frag_success; /* Traffic Class 6 Fragment Success */ + __le16 traffic_class7_frag_success; /* Traffic Class 7 Fragment Success */ + + __le16 num_frag_non_prime_rates; /* number of Fragments for non-prime rates */ + __le16 ack_timeout_non_prime_rates; /* ACK Timeout for non-prime rates */ + +} __attribute__((__packed__)); + + +struct agnx_beacon_hdr { + struct agnx_sta_power power; /* Tx Station Power Template */ + u8 phy_hdr[6]; /* PHY Hdr */ + u8 frame_len_lo; /* Frame Length Lo */ + u8 frame_len_hi; /* Frame Length Hi */ + u8 mac_hdr[24]; /* MAC Header */ + /* FIXME */ + /* 802.11(abg) beacon */ +} __attribute__((__packed__)); + +void hash_write(struct agnx_priv *priv, u8 *mac_addr, u8 sta_id); +void hash_dump(struct agnx_priv *priv, u8 sta_id); +void hash_read(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id); +void hash_delete(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id); + +void get_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx); +void set_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, + unsigned int sta_idx); +void get_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, + unsigned int sta_idx, unsigned int wq_idx); +void set_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, + unsigned int sta_idx, unsigned int wq_idx); +void get_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx); +void set_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx); + +void sta_power_init(struct agnx_priv *priv, unsigned int num); +void sta_init(struct agnx_priv *priv, unsigned int num); + +#endif /* AGNX_STA_H_ */ diff --git a/drivers/staging/agnx/table.c b/drivers/staging/agnx/table.c new file mode 100644 index 00000000000..c60048487b5 --- /dev/null +++ b/drivers/staging/agnx/table.c @@ -0,0 +1,168 @@ +#include <linux/pci.h> +#include <linux/delay.h> +#include "agnx.h" +#include "debug.h" +#include "phy.h" + +static const u32 +tx_fir_table[] = { 0x19, 0x5d, 0xce, 0x151, 0x1c3, 0x1ff, 0x1ea, 0x17c, 0xcf, + 0x19, 0x38e, 0x350, 0x362, 0x3ad, 0x5, 0x44, 0x59, 0x49, + 0x21, 0x3f7, 0x3e0, 0x3e3, 0x3f3, 0x0 }; + +void tx_fir_table_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int i; + + for (i = 0; i < ARRAY_SIZE(tx_fir_table); i++) + iowrite32(tx_fir_table[i], ctl + AGNX_FIR_BASE + i*4); +} /* fir_table_setup */ + + +static const u32 +gain_table[] = { 0x8, 0x8, 0xf, 0x13, 0x17, 0x1b, 0x1f, 0x23, 0x27, 0x2b, + 0x2f, 0x33, 0x37, 0x3b, 0x3f, 0x43, 0x47, 0x4b, 0x4f, + 0x53, 0x57, 0x5b, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f }; + +void gain_table_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + int i; + + for (i = 0; i < ARRAY_SIZE(gain_table); i++) { + iowrite32(gain_table[i], ctl + AGNX_GAIN_TABLE + i*4); + iowrite32(gain_table[i], ctl + AGNX_GAIN_TABLE + i*4 + 0x80); + } +} /* gain_table_init */ + +void monitor_gain_table_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + unsigned int i; + + for (i = 0; i < 0x44; i += 4) { + iowrite32(0x61, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0x61, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } + for (i = 0x44; i < 0x64; i += 4) { + iowrite32(0x6e, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0x6e, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } + for (i = 0x64; i < 0x94; i += 4) { + iowrite32(0x7a, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0x7a, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } + for (i = 0x94; i < 0xdc; i += 4) { + iowrite32(0x87, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0x87, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } + for (i = 0xdc; i < 0x148; i += 4) { + iowrite32(0x95, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0x95, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } + for (i = 0x148; i < 0x1e8; i += 4) { + iowrite32(0xa2, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0xa2, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } + for (i = 0x1e8; i <= 0x1fc; i += 4) { + iowrite32(0xb0, ctl + AGNX_MONGCR_BASE + i); + iowrite32(0xb0, ctl + AGNX_MONGCR_BASE + 0x200 + i); + } +} /* monitor_gain_table_init */ + + +void routing_table_init(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + unsigned int type, subtype; + u32 reg; + + disable_receiver(priv); + + for ( type = 0; type < 0x3; type++ ) { + for (subtype = 0; subtype < 0x10; subtype++) { + /* 1. Set Routing table to R/W and to Return status on Read */ + reg = (type << ROUTAB_TYPE_SHIFT) | + (subtype << ROUTAB_SUBTYPE_SHIFT); + reg |= (1 << ROUTAB_RW_SHIFT) | (1 << ROUTAB_STATUS_SHIFT); + if (type == ROUTAB_TYPE_DATA) { + /* NULL goes to RFP */ + if (subtype == ROUTAB_SUBTYPE_NULL) +// reg |= ROUTAB_ROUTE_RFP; + reg |= ROUTAB_ROUTE_CPU; + /* QOS NULL goes to CPU */ + else if (subtype == ROUTAB_SUBTYPE_QOSNULL) + reg |= ROUTAB_ROUTE_CPU; + /* All Data and QOS data subtypes go to Encryption */ + else if ((subtype == ROUTAB_SUBTYPE_DATA) || + (subtype == ROUTAB_SUBTYPE_DATAACK) || + (subtype == ROUTAB_SUBTYPE_DATAPOLL) || + (subtype == ROUTAB_SUBTYPE_DATAPOLLACK) || + (subtype == ROUTAB_SUBTYPE_QOSDATA) || + (subtype == ROUTAB_SUBTYPE_QOSDATAACK) || + (subtype == ROUTAB_SUBTYPE_QOSDATAPOLL) || + (subtype == ROUTAB_SUBTYPE_QOSDATAACKPOLL)) + reg |= ROUTAB_ROUTE_ENCRY; +// reg |= ROUTAB_ROUTE_CPU; + /*Drop NULL and QOS NULL ack, poll and poll ack*/ + else if ((subtype == ROUTAB_SUBTYPE_NULLACK) || + (subtype == ROUTAB_SUBTYPE_QOSNULLACK) || + (subtype == ROUTAB_SUBTYPE_NULLPOLL) || + (subtype == ROUTAB_SUBTYPE_QOSNULLPOLL) || + (subtype == ROUTAB_SUBTYPE_NULLPOLLACK) || + (subtype == ROUTAB_SUBTYPE_QOSNULLPOLLACK)) +// reg |= ROUTAB_ROUTE_DROP; + reg |= ROUTAB_ROUTE_CPU; + } + else + reg |= (ROUTAB_ROUTE_CPU); + iowrite32(reg, ctl + AGNX_RXM_ROUTAB); + /* Check to verify that the status bit cleared */ + routing_table_delay(); + } + } + enable_receiver(priv); +} /* routing_table_init */ + +void tx_engine_lookup_tbl_init(struct agnx_priv *priv) +{ + void __iomem *data = priv->data; + unsigned int i; + + for (i = 0; i <= 28; i += 4) + iowrite32(0xb00c, data + AGNX_ENGINE_LOOKUP_TBL + i); + for (i = 32; i <= 120; i += 8) { + iowrite32(0x1e58, data + AGNX_ENGINE_LOOKUP_TBL + i); + iowrite32(0xb00c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); + } + + for (i = 128; i <= 156; i += 4) + iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i); + for (i = 160; i <= 248; i += 8) { + iowrite32(0x1858, data + AGNX_ENGINE_LOOKUP_TBL + i); + iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); + } + + for (i = 256; i <= 284; i += 4) + iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i); + for (i = 288; i <= 376; i += 8) { + iowrite32(0x1a58, data + AGNX_ENGINE_LOOKUP_TBL + i); + iowrite32(0x1858, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); + } + + for (i = 512; i <= 540; i += 4) + iowrite32(0xc00c, data + AGNX_ENGINE_LOOKUP_TBL + i); + for (i = 544; i <= 632; i += 8) { + iowrite32(0x2058, data + AGNX_ENGINE_LOOKUP_TBL + i); + iowrite32(0xc00c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); + } + + for (i = 640; i <= 668; i += 4) + iowrite32(0xc80c, data + AGNX_ENGINE_LOOKUP_TBL + i); + for (i = 672; i <= 764; i += 8) { + iowrite32(0x2258, data + AGNX_ENGINE_LOOKUP_TBL + i); + iowrite32(0xc80c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); + } +} + diff --git a/drivers/staging/agnx/table.h b/drivers/staging/agnx/table.h new file mode 100644 index 00000000000..f0626b5ee86 --- /dev/null +++ b/drivers/staging/agnx/table.h @@ -0,0 +1,10 @@ +#ifndef AGNX_TABLE_H_ +#define AGNX_TABLE_H_ + +void tx_fir_table_init(struct agnx_priv *priv); +void gain_table_init(struct agnx_priv *priv); +void monitor_gain_table_init(struct agnx_priv *priv); +void routing_table_init(struct agnx_priv *priv); +void tx_engine_lookup_tbl_init(struct agnx_priv *priv); + +#endif /* AGNX_TABLE_H_ */ diff --git a/drivers/staging/agnx/xmit.c b/drivers/staging/agnx/xmit.c new file mode 100644 index 00000000000..42ed7d51b6e --- /dev/null +++ b/drivers/staging/agnx/xmit.c @@ -0,0 +1,819 @@ +/** + * Airgo MIMO wireless driver + * + * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> + + * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer + * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include "agnx.h" +#include "debug.h" +#include "phy.h" + +unsigned int rx_frame_cnt = 0; +//unsigned int local_tx_sent_cnt = 0; + +static inline void disable_rx_engine(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + iowrite32(0x100, ctl + AGNX_CIR_RXCTL); + /* Wait for RX Control to have the Disable Rx Interrupt (0x100) set */ + ioread32(ctl + AGNX_CIR_RXCTL); +} + +static inline void enable_rx_engine(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + iowrite32(0x80, ctl + AGNX_CIR_RXCTL); + ioread32(ctl + AGNX_CIR_RXCTL); +} + +inline void disable_rx_interrupt(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + disable_rx_engine(priv); + reg = ioread32(ctl + AGNX_CIR_RXCFG); + reg &= ~0x20; + iowrite32(reg, ctl + AGNX_CIR_RXCFG); + ioread32(ctl + AGNX_CIR_RXCFG); +} + +inline void enable_rx_interrupt(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + u32 reg; + + reg = ioread32(ctl + AGNX_CIR_RXCFG); + reg |= 0x20; + iowrite32(reg, ctl + AGNX_CIR_RXCFG); + ioread32(ctl + AGNX_CIR_RXCFG); + enable_rx_engine(priv); +} + +static inline void rx_desc_init(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->rx.desc + idx; + struct agnx_info *info = priv->rx.info + idx; + + memset(info, 0, sizeof(*info)); + + info->dma_len = IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct agnx_hdr); + info->skb = dev_alloc_skb(info->dma_len); + if (info->skb == NULL) + agnx_bug("refill err"); + + info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(info->skb), + info->dma_len, PCI_DMA_FROMDEVICE); + memset(desc, 0, sizeof(*desc)); + desc->dma_addr = cpu_to_be32(info->mapping); + /* Set the owner to the card */ + desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); +} + +static inline void rx_desc_reinit(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_info *info = priv->rx.info + idx; + + /* Cause ieee80211 will free the skb buffer, so we needn't to free it again?! */ + pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); + rx_desc_init(priv, idx); +} + +static inline void rx_desc_reusing(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->rx.desc + idx; + struct agnx_info *info = priv->rx.info + idx; + + memset(desc, 0, sizeof(*desc)); + desc->dma_addr = cpu_to_be32(info->mapping); + /* Set the owner to the card */ + desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); +} + +static void rx_desc_free(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->rx.desc + idx; + struct agnx_info *info = priv->rx.info + idx; + + BUG_ON(!desc || !info); + if (info->mapping) + pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); + if (info->skb) + dev_kfree_skb(info->skb); + memset(info, 0, sizeof(*info)); + memset(desc, 0, sizeof(*desc)); +} + +static inline void __tx_desc_free(struct agnx_priv *priv, + struct agnx_desc *desc, struct agnx_info *info) +{ + BUG_ON(!desc || !info); + /* TODO make sure mapping, skb and len are consistency */ + if (info->mapping) + pci_unmap_single(priv->pdev, info->mapping, + info->dma_len, PCI_DMA_TODEVICE); + if (info->type == PACKET) + dev_kfree_skb(info->skb); + + memset(info, 0, sizeof(*info)); + memset(desc, 0, sizeof(*desc)); +} + +static void txm_desc_free(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->txm.desc + idx; + struct agnx_info *info = priv->txm.info + idx; + + __tx_desc_free(priv, desc, info); +} + +static void txd_desc_free(struct agnx_priv *priv, unsigned int idx) +{ + struct agnx_desc *desc = priv->txd.desc + idx; + struct agnx_info *info = priv->txd.info + idx; + + __tx_desc_free(priv, desc, info); +} + +int fill_rings(struct agnx_priv *priv) +{ + void __iomem *ctl = priv->ctl; + unsigned int i; + u32 reg; + AGNX_TRACE; + + priv->txd.idx_sent = priv->txm.idx_sent = 0; + priv->rx.idx = priv->txm.idx = priv->txd.idx = 0; + + for (i = 0; i < priv->rx.size; i++) + rx_desc_init(priv, i); + for (i = 0; i < priv->txm.size; i++) { + memset(priv->txm.desc + i, 0, sizeof(struct agnx_desc)); + memset(priv->txm.info + i, 0, sizeof(struct agnx_info)); + } + for (i = 0; i < priv->txd.size; i++) { + memset(priv->txd.desc + i, 0, sizeof(struct agnx_desc)); + memset(priv->txd.info + i, 0, sizeof(struct agnx_info)); + } + + /* FIXME Set the card RX TXM and TXD address */ + agnx_write32(ctl, AGNX_CIR_RXCMSTART, priv->rx.dma); + agnx_write32(ctl, AGNX_CIR_RXCMEND, priv->txm.dma); + + agnx_write32(ctl, AGNX_CIR_TXMSTART, priv->txm.dma); + agnx_write32(ctl, AGNX_CIR_TXMEND, priv->txd.dma); + + agnx_write32(ctl, AGNX_CIR_TXDSTART, priv->txd.dma); + agnx_write32(ctl, AGNX_CIR_TXDEND, priv->txd.dma + + sizeof(struct agnx_desc) * priv->txd.size); + + /* FIXME Relinquish control of rings to card */ + reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); + reg &= ~0x800; + agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); + return 0; +} /* fill_rings */ + +void unfill_rings(struct agnx_priv *priv) +{ + unsigned long flags; + unsigned int i; + AGNX_TRACE; + + spin_lock_irqsave(&priv->lock, flags); + + for (i = 0; i < priv->rx.size; i++) + rx_desc_free(priv, i); + for (i = 0; i < priv->txm.size; i++) + txm_desc_free(priv, i); + for (i = 0; i < priv->txd.size; i++) + txd_desc_free(priv, i); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/* Extract the bitrate out of a CCK PLCP header. + copy from bcm43xx driver */ +static inline u8 agnx_plcp_get_bitrate_cck(__be32 *phyhdr_11b) +{ + /* FIXME */ + switch (*(u8 *)phyhdr_11b) { + case 0x0A: + return 0; + case 0x14: + return 1; + case 0x37: + return 2; + case 0x6E: + return 3; + } + agnx_bug("Wrong plcp rate"); + return 0; +} + +/* FIXME */ +static inline u8 agnx_plcp_get_bitrate_ofdm(__be32 *phyhdr_11g) +{ + u8 rate = *(u8 *)phyhdr_11g & 0xF; + + printk(PFX "G mode rate is 0x%x\n", rate); + return rate; +} + +/* FIXME */ +static void get_rx_stats(struct agnx_priv *priv, struct agnx_hdr *hdr, + struct ieee80211_rx_status *stat) +{ + void __iomem *ctl = priv->ctl; + u8 *rssi; + u32 noise; + /* FIXME just for test */ + int snr = 40; /* signal-to-noise ratio */ + + memset(stat, 0, sizeof(*stat)); + /* RSSI */ + rssi = (u8 *)&hdr->phy_stats_lo; +// stat->ssi = (rssi[0] + rssi[1] + rssi[2]) / 3; + /* Noise */ + noise = ioread32(ctl + AGNX_GCR_NOISE0); + noise += ioread32(ctl + AGNX_GCR_NOISE1); + noise += ioread32(ctl + AGNX_GCR_NOISE2); + stat->noise = noise / 3; + /* Signal quality */ + //snr = stat->ssi - stat->noise; + if (snr >=0 && snr < 40) + stat->signal = 5 * snr / 2; + else if (snr >= 40) + stat->signal = 100; + else + stat->signal = 0; + + + if (hdr->_11b0 && !hdr->_11g0) { + stat->rate_idx = agnx_plcp_get_bitrate_cck(&hdr->_11b0); + } else if (!hdr->_11b0 && hdr->_11g0) { + printk(PFX "RX: Found G mode packet\n"); + stat->rate_idx = agnx_plcp_get_bitrate_ofdm(&hdr->_11g0); + } else + agnx_bug("Unknown packets type"); + + + stat->band = IEEE80211_BAND_2GHZ; + stat->freq = agnx_channels[priv->channel - 1].center_freq; +// stat->antenna = 3; +// stat->mactime = be32_to_cpu(hdr->time_stamp); +// stat->channel = priv->channel; + +} + +static inline void combine_hdr_frag(struct ieee80211_hdr *ieeehdr, + struct sk_buff *skb) +{ + u16 fctl; + unsigned int hdrlen; + + fctl = le16_to_cpu(ieeehdr->frame_control); + hdrlen = ieee80211_hdrlen(fctl); + /* FIXME */ + if (hdrlen < (2+2+6)/*minimum hdr*/ || + hdrlen > sizeof(struct ieee80211_mgmt)) { + printk(KERN_ERR PFX "hdr len is %d\n", hdrlen); + agnx_bug("Wrong ieee80211 hdr detected"); + } + skb_push(skb, hdrlen); + memcpy(skb->data, ieeehdr, hdrlen); +} /* combine_hdr_frag */ + +static inline int agnx_packet_check(struct agnx_priv *priv, struct agnx_hdr *agnxhdr, + unsigned packet_len) +{ + if (agnx_get_bits(CRC_FAIL, CRC_FAIL_SHIFT, be32_to_cpu(agnxhdr->reg1)) == 1){ + printk(PFX "RX: CRC check fail\n"); + goto drop; + } + if (packet_len > 2048) { + printk(PFX "RX: Too long packet detected\n"); + goto drop; + } + + /* FIXME Just usable for Promious Mode, for Manage mode exclude FCS */ +/* if (packet_len - sizeof(*agnxhdr) < FCS_LEN) { */ +/* printk(PFX "RX: Too short packet detected\n"); */ +/* goto drop; */ +/* } */ + return 0; +drop: + priv->stats.dot11FCSErrorCount++; + return -1; +} + +void handle_rx_irq(struct agnx_priv *priv) +{ + struct ieee80211_rx_status status; + unsigned int len; +// AGNX_TRACE; + + do { + struct agnx_desc *desc; + u32 frag; + struct agnx_info *info; + struct agnx_hdr *hdr; + struct sk_buff *skb; + unsigned int i = priv->rx.idx % priv->rx.size; + + desc = priv->rx.desc + i; + frag = be32_to_cpu(desc->frag); + if (frag & OWNER) + break; + + info = priv->rx.info + i; + skb = info->skb; + hdr = (struct agnx_hdr *)(skb->data); + + len = (frag & PACKET_LEN) >> PACKET_LEN_SHIFT; + if (agnx_packet_check(priv, hdr, len) == -1) { + rx_desc_reusing(priv, i); + continue; + } + skb_put(skb, len); + + do { + u16 fctl; + fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr)->frame_control); + if ((fctl & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_BEACON)// && !(fctl & IEEE80211_STYPE_BEACON)) + dump_ieee80211_hdr((struct ieee80211_hdr *)hdr->mac_hdr, "RX"); + } while (0); + + if (hdr->_11b0 && !hdr->_11g0) { +/* int j; */ +/* u16 fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr) */ +/* ->frame_control); */ +/* if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { */ +/* agnx_print_rx_hdr(hdr); */ +// agnx_print_sta(priv, BSSID_STAID); +/* for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ +/* } */ + + get_rx_stats(priv, hdr, &status); + skb_pull(skb, sizeof(*hdr)); + combine_hdr_frag((struct ieee80211_hdr *)hdr->mac_hdr, skb); + } else if (!hdr->_11b0 && hdr->_11g0) { +// int j; + agnx_print_rx_hdr(hdr); + agnx_print_sta(priv, BSSID_STAID); +// for (j = 0; j < 8; j++) + agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); + + print_hex_dump_bytes("agnx: RX_PACKET: ", DUMP_PREFIX_NONE, + skb->data, skb->len + 8); + +// if (agnx_plcp_get_bitrate_ofdm(&hdr->_11g0) == 0) + get_rx_stats(priv, hdr, &status); + skb_pull(skb, sizeof(*hdr)); + combine_hdr_frag((struct ieee80211_hdr *) + ((void *)&hdr->mac_hdr), skb); +// dump_ieee80211_hdr((struct ieee80211_hdr *)skb->data, "RX G"); + } else + agnx_bug("Unknown packets type"); + ieee80211_rx_irqsafe(priv->hw, skb, &status); + rx_desc_reinit(priv, i); + + } while ( priv->rx.idx++ ); +} /* handle_rx_irq */ + +static inline void handle_tx_irq(struct agnx_priv *priv, struct agnx_ring *ring) +{ + struct agnx_desc *desc; + struct agnx_info *info; + unsigned int idx; + + for (idx = ring->idx_sent; idx < ring->idx; idx++) { + unsigned int i = idx % ring->size; + u32 frag; + + desc = ring->desc + i; + info = ring->info + i; + + frag = be32_to_cpu(desc->frag); + if (frag & OWNER) { + if (info->type == HEADER) + break; + else + agnx_bug("TX error"); + } + + pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_TODEVICE); + + do { +// int j; + size_t len; + len = info->skb->len - sizeof(struct agnx_hdr) + info->hdr_len; + // if (len == 614) { +// agnx_print_desc(desc); + if (info->type == PACKET) { +// agnx_print_tx_hdr((struct agnx_hdr *)info->skb->data); +/* agnx_print_sta_power(priv, LOCAL_STAID); */ +/* agnx_print_sta(priv, LOCAL_STAID); */ +/* // for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, 0); */ +// agnx_print_sta_power(priv, BSSID_STAID); +// agnx_print_sta(priv, BSSID_STAID); +// for (j = 0; j < 8; j++) +// agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); + } +// } + } while (0); + + if (info->type == PACKET) { +// dump_txm_registers(priv); +// dump_rxm_registers(priv); +// dump_bm_registers(priv); +// dump_cir_registers(priv); + } + + if (info->type == PACKET) { +// struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(info->skb); + + skb_pull(info->skb, sizeof(struct agnx_hdr)); + memcpy(skb_push(info->skb, info->hdr_len), &info->hdr, info->hdr_len); + +// dump_ieee80211_hdr((struct ieee80211_hdr *)info->skb->data, "TX_HANDLE"); +/* print_hex_dump_bytes("agnx: TX_HANDLE: ", DUMP_PREFIX_NONE, */ +/* info->skb->data, info->skb->len); */ + + if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) + txi->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(priv->hw, info->skb); + + +/* info->tx_status.queue_number = (ring->size - i) / 2; */ +/* ieee80211_tx_status_irqsafe(priv->hw, info->skb, &(info->tx_status)); */ +/* } else */ +/* dev_kfree_skb_irq(info->skb); */ + } + memset(desc, 0, sizeof(*desc)); + memset(info, 0, sizeof(*info)); + } + + ring->idx_sent = idx; + /* TODO fill the priv->low_level_stats */ + + /* ieee80211_wake_queue(priv->hw, 0); */ +} + +void handle_txm_irq(struct agnx_priv *priv) +{ + handle_tx_irq(priv, &priv->txm); +} + +void handle_txd_irq(struct agnx_priv *priv) +{ + handle_tx_irq(priv, &priv->txd); +} + +void handle_other_irq(struct agnx_priv *priv) +{ +// void __iomem *ctl = priv->ctl; + u32 status = priv->irq_status; + void __iomem *ctl = priv->ctl; + u32 reg; + + if (status & IRQ_TX_BEACON) { + iowrite32(IRQ_TX_BEACON, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: TX Beacon control is 0X%.8X\n", ioread32(ctl + AGNX_TXM_BEACON_CTL)); + printk(PFX "IRQ: TX Beacon rx frame num: %d\n", rx_frame_cnt); + } + if (status & IRQ_TX_RETRY) { + reg = ioread32(ctl + AGNX_TXM_RETRYSTAID); + printk(PFX "IRQ: TX Retry, RETRY STA ID is %x\n", reg); + } + if (status & IRQ_TX_ACTIVITY) + printk(PFX "IRQ: TX Activity\n"); + if (status & IRQ_RX_ACTIVITY) + printk(PFX "IRQ: RX Activity\n"); + if (status & IRQ_RX_X) + printk(PFX "IRQ: RX X\n"); + if (status & IRQ_RX_Y) { + reg = ioread32(ctl + AGNX_INT_MASK); + reg &= ~IRQ_RX_Y; + iowrite32(reg, ctl + AGNX_INT_MASK); + iowrite32(IRQ_RX_Y, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: RX Y\n"); + } + if (status & IRQ_RX_HASHHIT) { + reg = ioread32(ctl + AGNX_INT_MASK); + reg &= ~IRQ_RX_HASHHIT; + iowrite32(reg, ctl + AGNX_INT_MASK); + iowrite32(IRQ_RX_HASHHIT, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: RX Hash Hit\n"); + + } + if (status & IRQ_RX_FRAME) { + reg = ioread32(ctl + AGNX_INT_MASK); + reg &= ~IRQ_RX_FRAME; + iowrite32(reg, ctl + AGNX_INT_MASK); + iowrite32(IRQ_RX_FRAME, ctl + AGNX_INT_STAT); + printk(PFX "IRQ: RX Frame\n"); + rx_frame_cnt++; + } + if (status & IRQ_ERR_INT) { + iowrite32(IRQ_ERR_INT, ctl + AGNX_INT_STAT); +// agnx_hw_reset(priv); + printk(PFX "IRQ: Error Interrupt\n"); + } + if (status & IRQ_TX_QUE_FULL) + printk(PFX "IRQ: TX Workqueue Full\n"); + if (status & IRQ_BANDMAN_ERR) + printk(PFX "IRQ: Bandwidth Management Error\n"); + if (status & IRQ_TX_DISABLE) + printk(PFX "IRQ: TX Disable\n"); + if (status & IRQ_RX_IVASESKEY) + printk(PFX "IRQ: RX Invalid Session Key\n"); + if (status & IRQ_REP_THHIT) + printk(PFX "IRQ: Replay Threshold Hit\n"); + if (status & IRQ_TIMER1) + printk(PFX "IRQ: Timer1\n"); + if (status & IRQ_TIMER_CNT) + printk(PFX "IRQ: Timer Count\n"); + if (status & IRQ_PHY_FASTINT) + printk(PFX "IRQ: Phy Fast Interrupt\n"); + if (status & IRQ_PHY_SLOWINT) + printk(PFX "IRQ: Phy Slow Interrupt\n"); + if (status & IRQ_OTHER) + printk(PFX "IRQ: 0x80000000\n"); +} /* handle_other_irq */ + + +static inline void route_flag_set(struct agnx_hdr *txhdr) +{ +// u32 reg = 0; + + /* FIXME */ +/* reg = (0x7 << ROUTE_COMPRESSION_SHIFT) & ROUTE_COMPRESSION; */ +/* txhdr->reg5 = cpu_to_be32(reg); */ + txhdr->reg5 = (0xa << 0x0) | (0x7 << 0x18); +// txhdr->reg5 = cpu_to_be32((0xa << 0x0) | (0x7 << 0x18)); +// txhdr->reg5 = cpu_to_be32(0x7 << 0x0); +} + +/* Return 0 if no match */ +static inline unsigned int get_power_level(unsigned int rate, unsigned int antennas_num) +{ + unsigned int power_level; + + switch (rate) { + case 10: + case 20: + case 55: + case 60: + case 90: + case 120: power_level = 22; break; + case 180: power_level = 19; break; + case 240: power_level = 18; break; + case 360: power_level = 16; break; + case 480: power_level = 15; break; + case 540: power_level = 14; break; + default: + agnx_bug("Error rate setting\n"); + } + + if (power_level && (antennas_num == 2)) + power_level -= 3; + + return power_level; +} + +static inline void fill_agnx_hdr(struct agnx_priv *priv, struct agnx_info *tx_info) +{ + struct agnx_hdr *txhdr = (struct agnx_hdr *)tx_info->skb->data; + size_t len; + u16 fc = le16_to_cpu(*(__le16 *)&tx_info->hdr); + u32 reg; + + memset(txhdr, 0, sizeof(*txhdr)); + +// reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, LOCAL_STAID); + reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, BSSID_STAID); + reg |= agnx_set_bits(WORKQUEUE_ID, WORKQUEUE_ID_SHIFT, 0); + txhdr->reg4 = cpu_to_be32(reg); + + /* Set the Hardware Sequence Number to 1? */ + reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 0); +// reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 1); + reg |= agnx_set_bits(MAC_HDR_LEN, MAC_HDR_LEN_SHIFT, tx_info->hdr_len); + txhdr->reg1 = cpu_to_be32(reg); + /* Set the agnx_hdr's MAC header */ + memcpy(txhdr->mac_hdr, &tx_info->hdr, tx_info->hdr_len); + + reg = agnx_set_bits(ACK, ACK_SHIFT, 1); +// reg = agnx_set_bits(ACK, ACK_SHIFT, 0); + reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 0); +// reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 1); + reg |= agnx_set_bits(RELAY, RELAY_SHIFT, 0); + reg |= agnx_set_bits(TM, TM_SHIFT, 0); + txhdr->reg0 = cpu_to_be32(reg); + + /* Set the long and short retry limits */ + txhdr->tx.short_retry_limit = tx_info->txi->control.retry_limit; + txhdr->tx.long_retry_limit = tx_info->txi->control.retry_limit; + + /* FIXME */ + len = tx_info->skb->len - sizeof(*txhdr) + tx_info->hdr_len + FCS_LEN; + if (fc & IEEE80211_FCTL_PROTECTED) + len += 8; + len = 2398; + reg = agnx_set_bits(FRAG_SIZE, FRAG_SIZE_SHIFT, len); + len = tx_info->skb->len - sizeof(*txhdr); + reg |= agnx_set_bits(PAYLOAD_LEN, PAYLOAD_LEN_SHIFT, len); + txhdr->reg3 = cpu_to_be32(reg); + + route_flag_set(txhdr); +} /* fill_hdr */ + +static void txm_power_set(struct agnx_priv *priv, + struct ieee80211_tx_info *txi) +{ + struct agnx_sta_power power; + u32 reg; + + /* FIXME */ + if (txi->tx_rate_idx < 0) { + /* For B mode Short Preamble */ + reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_SHORT); +// control->tx_rate = -control->tx_rate; + } else + reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211G); +// reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_LONG); + reg |= agnx_set_bits(SIGNAL, SIGNAL_SHIFT, 0xB); + reg |= agnx_set_bits(RATE, RATE_SHIFT, 0xB); +// reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 15); + reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 20); + /* if rate < 11M set it to 0 */ + reg |= agnx_set_bits(NUM_TRANSMITTERS, NUM_TRANSMITTERS_SHIFT, 1); +// reg |= agnx_set_bits(EDCF, EDCF_SHIFT, 1); +// reg |= agnx_set_bits(TIFS, TIFS_SHIFT, 1); + + power.reg = reg; +// power.reg = cpu_to_le32(reg); + +// set_sta_power(priv, &power, LOCAL_STAID); + set_sta_power(priv, &power, BSSID_STAID); +} + +static inline int tx_packet_check(struct sk_buff *skb) +{ + unsigned int ieee_len = ieee80211_get_hdrlen_from_skb(skb); + if (skb->len > 2048) { + printk(KERN_ERR PFX "length is %d\n", skb->len); + agnx_bug("Too long TX skb"); + return -1; + } + /* FIXME */ + if (skb->len == ieee_len) { + printk(PFX "A strange TX packet\n"); + return -1; + /* tx_faile_irqsafe(); */ + } + return 0; +} + +static int __agnx_tx(struct agnx_priv *priv, struct sk_buff *skb, + struct agnx_ring *ring) +{ + struct agnx_desc *hdr_desc, *frag_desc; + struct agnx_info *hdr_info, *frag_info; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&priv->lock, flags); + + /* The RX interrupt need be Disable until this TX packet + is handled in the next tx interrupt */ + disable_rx_interrupt(priv); + + i = ring->idx; + ring->idx += 2; +/* if (priv->txm_idx - priv->txm_idx_sent == AGNX_TXM_RING_SIZE - 2) */ +/* ieee80211_stop_queue(priv->hw, 0); */ + + /* Set agnx header's info and desc */ + i %= ring->size; + hdr_desc = ring->desc + i; + hdr_info = ring->info + i; + hdr_info->hdr_len = ieee80211_get_hdrlen_from_skb(skb); + memcpy(&hdr_info->hdr, skb->data, hdr_info->hdr_len); + + /* Add the agnx header to the front of the SKB */ + skb_push(skb, sizeof(struct agnx_hdr) - hdr_info->hdr_len); + + hdr_info->txi = txi; + hdr_info->dma_len = sizeof(struct agnx_hdr); + hdr_info->skb = skb; + hdr_info->type = HEADER; + fill_agnx_hdr(priv, hdr_info); + hdr_info->mapping = pci_map_single(priv->pdev, skb->data, + hdr_info->dma_len, PCI_DMA_TODEVICE); + do { + u32 frag = 0; + frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 1); + frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 0); + frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); + frag |= agnx_set_bits(FIRST_FRAG_LEN, FIRST_FRAG_LEN_SHIFT, 1); + frag |= agnx_set_bits(OWNER, OWNER_SHIFT, 1); + hdr_desc->frag = cpu_to_be32(frag); + } while (0); + hdr_desc->dma_addr = cpu_to_be32(hdr_info->mapping); + + + /* Set Frag's info and desc */ + i = (i + 1) % ring->size; + frag_desc = ring->desc + i; + frag_info = ring->info + i; + memcpy(frag_info, hdr_info, sizeof(struct agnx_info)); + frag_info->type = PACKET; + frag_info->dma_len = skb->len - hdr_info->dma_len; + frag_info->mapping = pci_map_single(priv->pdev, skb->data + hdr_info->dma_len, + frag_info->dma_len, PCI_DMA_TODEVICE); + do { + u32 frag = 0; + frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 0); + frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 1); + frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); + frag |= agnx_set_bits(SUB_FRAG_LEN, SUB_FRAG_LEN_SHIFT, frag_info->dma_len); + frag_desc->frag = cpu_to_be32(frag); + } while (0); + frag_desc->dma_addr = cpu_to_be32(frag_info->mapping); + + txm_power_set(priv, txi); + +/* do { */ +/* int j; */ +/* size_t len; */ +/* len = skb->len - hdr_info->dma_len + hdr_info->hdr_len; */ +/* // if (len == 614) { */ +/* agnx_print_desc(hdr_desc); */ +/* agnx_print_desc(frag_desc); */ +/* agnx_print_tx_hdr((struct agnx_hdr *)skb->data); */ +/* agnx_print_sta_power(priv, LOCAL_STAID); */ +/* agnx_print_sta(priv, LOCAL_STAID); */ +/* for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, j); */ +/* agnx_print_sta_power(priv, BSSID_STAID); */ +/* agnx_print_sta(priv, BSSID_STAID); */ +/* for (j = 0; j < 8; j++) */ +/* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ +/* // } */ +/* } while (0); */ + + spin_unlock_irqrestore(&priv->lock, flags); + + /* FIXME ugly code */ + /* Trigger TXM */ + do { + u32 reg; + reg = (ioread32(priv->ctl + AGNX_CIR_TXMCTL)); + reg |= 0x8; + iowrite32((reg), priv->ctl + AGNX_CIR_TXMCTL); + }while (0); + + /* Trigger TXD */ + do { + u32 reg; + reg = (ioread32(priv->ctl + AGNX_CIR_TXDCTL)); + reg |= 0x8; + iowrite32((reg), priv->ctl + AGNX_CIR_TXDCTL); + }while (0); + + return 0; +} + +int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb) +{ + u16 fctl; + + if (tx_packet_check(skb)) + return 0; + +/* print_hex_dump_bytes("agnx: TX_PACKET: ", DUMP_PREFIX_NONE, */ +/* skb->data, skb->len); */ + + fctl = le16_to_cpu(*((__le16 *)skb->data)); + + if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ) + return __agnx_tx(priv, skb, &priv->txd); + else + return __agnx_tx(priv, skb, &priv->txm); +} diff --git a/drivers/staging/agnx/xmit.h b/drivers/staging/agnx/xmit.h new file mode 100644 index 00000000000..93ac4157e69 --- /dev/null +++ b/drivers/staging/agnx/xmit.h @@ -0,0 +1,250 @@ +#ifndef AGNX_XMIT_H_ +#define AGNX_XMIT_H_ + +#include <net/mac80211.h> + +struct agnx_priv; + +static inline u32 agnx_set_bits(u32 mask, u8 shift, u32 value) +{ + return (value << shift) & mask; +} + +static inline u32 agnx_get_bits(u32 mask, u8 shift, u32 value) +{ + return (value & mask) >> shift; +} + + +struct agnx_rx { + __be16 rx_packet_duration; /* RX Packet Duration */ + __be16 replay_cnt; /* Replay Count */ +} __attribute__((__packed__)); + + +struct agnx_tx { + u8 long_retry_limit; /* Long Retry Limit */ + u8 short_retry_limit; /* Short Retry Limit */ + u8 long_retry_cnt; /* Long Retry Count */ + u8 short_retry_cnt; /* Short Retry Count */ +} __attribute__((__packed__)); + + +/* Copy from bcm43xx */ +#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] +#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) +#define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__, nr_bytes) + +#define P4D_BIT3S(magic, nr_bits) __be32 __padding##magic:nr_bits +#define P4D_BITS(line, nr_bits) P4D_BIT3S(line, nr_bits) +#define PAD_BITS(nr_bits) P4D_BITS(__LINE__, nr_bits) + + +struct agnx_hdr { + __be32 reg0; +#define RTS 0x80000000 /* RTS */ +#define RTS_SHIFT 31 +#define MULTICAST 0x40000000 /* multicast */ +#define MULTICAST_SHIFT 30 +#define ACK 0x30000000 /* ACK */ +#define ACK_SHIFT 28 +#define TM 0x08000000 /* TM */ +#define TM_SHIFT 27 +#define RELAY 0x04000000 /* Relay */ +#define RELAY_SHIFT 26 +/* PAD_BITS(4); */ +#define REVISED_FCS 0x00380000 /* revised FCS */ +#define REVISED_FCS_SHIFT 19 +#define NEXT_BUFFER_ADDR 0x0007FFFF /* Next Buffer Address */ +#define NEXT_BUFFER_ADDR_SHIFT 0 + + __be32 reg1; +#define MAC_HDR_LEN 0xFC000000 /* MAC Header Length */ +#define MAC_HDR_LEN_SHIFT 26 +#define DURATION_OVERIDE 0x02000000 /* Duration Override */ +#define DURATION_OVERIDE_SHIFT 25 +#define PHY_HDR_OVERIDE 0x01000000 /* PHY Header Override */ +#define PHY_HDR_OVERIDE_SHIFT 24 +#define CRC_FAIL 0x00800000 /* CRC fail */ +#define CRC_FAIL_SHIFT 23 +/* PAD_BITS(1); */ +#define SEQUENCE_NUMBER 0x00200000 /* Sequence Number */ +#define SEQUENCE_NUMBER_SHIFT 21 +/* PAD_BITS(2); */ +#define BUFF_HEAD_ADDR 0x0007FFFF /* Buffer Head Address */ +#define BUFF_HEAD_ADDR_SHIFT 0 + + __be32 reg2; +#define PDU_COUNT 0xFC000000 /* PDU Count */ +#define PDU_COUNT_SHIFT 26 +/* PAD_BITS(3); */ +#define WEP_KEY 0x00600000 /* WEP Key # */ +#define WEP_KEY_SHIFT 21 +#define USES_WEP_KEY 0x00100000 /* Uses WEP Key */ +#define USES_WEP_KEY_SHIFT 20 +#define KEEP_ALIVE 0x00080000 /* Keep alive */ +#define KEEP_ALIVE_SHIFT 19 +#define BUFF_TAIL_ADDR 0x0007FFFF /* Buffer Tail Address */ +#define BUFF_TAIL_ADDR_SHIFT 0 + + __be32 reg3; +#define CTS_11G 0x80000000 /* CTS in 11g */ +#define CTS_11G_SHIFT 31 +#define RTS_11G 0x40000000 /* RTS in 11g */ +#define RTS_11G_SHIFT 30 +/* PAD_BITS(2); */ +#define FRAG_SIZE 0x0FFF0000 /* fragment size */ +#define FRAG_SIZE_SHIFT 16 +#define PAYLOAD_LEN 0x0000FFF0 /* payload length */ +#define PAYLOAD_LEN_SHIFT 4 +#define FRAG_NUM 0x0000000F /* number of frags */ +#define FRAG_NUM_SHIFT 0 + + __be32 reg4; +/* PAD_BITS(4); */ +#define RELAY_STAID 0x0FFF0000 /* relayStald */ +#define RELAY_STAID_SHIFT 16 +#define STATION_ID 0x0000FFF0 /* Station ID */ +#define STATION_ID_SHIFT 4 +#define WORKQUEUE_ID 0x0000000F /* Workqueue ID */ +#define WORKQUEUE_ID_SHIFT 0 + + /* FIXME this register maybe is LE? */ + __be32 reg5; +/* PAD_BITS(4); */ +#define ROUTE_HOST 0x0F000000 +#define ROUTE_HOST_SHIFT 24 +#define ROUTE_CARD_CPU 0x00F00000 +#define ROUTE_CARD_CPU_SHIFT 20 +#define ROUTE_ENCRYPTION 0x000F0000 +#define ROUTE_ENCRYPTION_SHIFT 16 +#define ROUTE_TX 0x0000F000 +#define ROUTE_TX_SHIFT 12 +#define ROUTE_RX1 0x00000F00 +#define ROUTE_RX1_SHIFT 8 +#define ROUTE_RX2 0x000000F0 +#define ROUTE_RX2_SHIFT 4 +#define ROUTE_COMPRESSION 0x0000000F +#define ROUTE_COMPRESSION_SHIFT 0 + + __be32 _11g0; /* 11g */ + __be32 _11g1; /* 11g */ + __be32 _11b0; /* 11b */ + __be32 _11b1; /* 11b */ + u8 mac_hdr[32]; /* MAC header */ + + __be16 rts_duration; /* RTS duration */ + __be16 last_duration; /* Last duration */ + __be16 sec_last_duration; /* Second to Last duration */ + __be16 other_duration; /* Other duration */ + __be16 tx_last_duration; /* TX Last duration */ + __be16 tx_other_duration; /* TX Other Duration */ + __be16 last_11g_len; /* Length of last 11g */ + __be16 other_11g_len; /* Lenght of other 11g */ + + __be16 last_11b_len; /* Length of last 11b */ + __be16 other_11b_len; /* Lenght of other 11b */ + + + __be16 reg6; +#define MBF 0xF000 /* mbf */ +#define MBF_SHIFT 12 +#define RSVD4 0x0FFF /* rsvd4 */ +#define RSVD4_SHIFT 0 + + __be16 rx_frag_stat; /* RX fragmentation status */ + + __be32 time_stamp; /* TimeStamp */ + __be32 phy_stats_hi; /* PHY stats hi */ + __be32 phy_stats_lo; /* PHY stats lo */ + __be32 mic_key0; /* MIC key 0 */ + __be32 mic_key1; /* MIC key 1 */ + + union { /* RX/TX Union */ + struct agnx_rx rx; + struct agnx_tx tx; + }; + + u8 rx_channel; /* Recieve Channel */ + PAD_BYTES(3); + + u8 reserved[4]; +} __attribute__((__packed__)); + + +struct agnx_desc { +#define PACKET_LEN 0xFFF00000 +#define PACKET_LEN_SHIFT 20 +/* ------------------------------------------------ */ +#define FIRST_PACKET_MASK 0x00080000 +#define FIRST_PACKET_MASK_SHIFT 19 +#define FIRST_RESERV2 0x00040000 +#define FIRST_RESERV2_SHIFT 18 +#define FIRST_TKIP_ERROR 0x00020000 +#define FIRST_TKIP_ERROR_SHIFT 17 +#define FIRST_TKIP_PACKET 0x00010000 +#define FIRST_TKIP_PACKET_SHIFT 16 +#define FIRST_RESERV1 0x0000F000 +#define FIRST_RESERV1_SHIFT 12 +#define FIRST_FRAG_LEN 0x00000FF8 +#define FIRST_FRAG_LEN_SHIFT 3 +/* ------------------------------------------------ */ +#define SUB_RESERV2 0x000c0000 +#define SUB_RESERV2_SHIFT 18 +#define SUB_TKIP_ERROR 0x00020000 +#define SUB_TKIP_ERROR_SHIFT 17 +#define SUB_TKIP_PACKET 0x00010000 +#define SUB_TKIP_PACKET_SHIFT 16 +#define SUB_RESERV1 0x00008000 +#define SUB_RESERV1_SHIFT 15 +#define SUB_FRAG_LEN 0x00007FF8 +#define SUB_FRAG_LEN_SHIFT 3 +/* ------------------------------------------------ */ +#define FIRST_FRAG 0x00000004 +#define FIRST_FRAG_SHIFT 2 +#define LAST_FRAG 0x00000002 +#define LAST_FRAG_SHIFT 1 +#define OWNER 0x00000001 +#define OWNER_SHIFT 0 + __be32 frag; + __be32 dma_addr; +} __attribute__((__packed__)); + +enum {HEADER, PACKET}; + +struct agnx_info { + struct sk_buff *skb; + dma_addr_t mapping; + u32 dma_len; /* dma buffer len */ + /* Below fields only usful for tx */ + u32 hdr_len; /* ieee80211 header length */ + unsigned int type; + struct ieee80211_tx_info *txi; + struct ieee80211_hdr hdr; +}; + + +struct agnx_ring { + struct agnx_desc *desc; + dma_addr_t dma; + struct agnx_info *info; + /* Will lead to overflow when sent packet number enough? */ + unsigned int idx; + unsigned int idx_sent; /* only usful for txd and txm */ + unsigned int size; +}; + +#define AGNX_RX_RING_SIZE 128 +#define AGNX_TXD_RING_SIZE 256 +#define AGNX_TXM_RING_SIZE 128 + +void disable_rx_interrupt(struct agnx_priv *priv); +void enable_rx_interrupt(struct agnx_priv *priv); +int fill_rings(struct agnx_priv *priv); +void unfill_rings(struct agnx_priv *priv); +void handle_rx_irq(struct agnx_priv *priv); +void handle_txd_irq(struct agnx_priv *priv); +void handle_txm_irq(struct agnx_priv *priv); +void handle_other_irq(struct agnx_priv *priv); +int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb); +#endif /* AGNX_XMIT_H_ */ |