From 3cfeb0c33f5cbcc6dde371392877ef3101b8f805 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 20 Dec 2010 15:16:53 -0500 Subject: rtl818x: move rtl8180 and rtl8187 to separate subdirectories These drivers share one header file, but nothing else. Worse, both drivers use the rtl8225 part with different register settings. The results has been some ugly naming -- let's simplify that. Signed-off-by: John W. Linville Acked-by: Hin-Tak Leung Acked-by: Larry Finger Acked-by: Herton Ronaldo Krzesinski --- drivers/net/wireless/rtl818x/Makefile | 9 +- drivers/net/wireless/rtl818x/rtl8180.h | 130 -- drivers/net/wireless/rtl818x/rtl8180/Makefile | 5 + drivers/net/wireless/rtl818x/rtl8180/dev.c | 1188 ++++++++++++++++++ drivers/net/wireless/rtl818x/rtl8180/grf5101.c | 190 +++ drivers/net/wireless/rtl818x/rtl8180/grf5101.h | 28 + drivers/net/wireless/rtl818x/rtl8180/max2820.c | 169 +++ drivers/net/wireless/rtl818x/rtl8180/max2820.h | 28 + drivers/net/wireless/rtl818x/rtl8180/rtl8180.h | 130 ++ drivers/net/wireless/rtl818x/rtl8180/rtl8225.c | 791 ++++++++++++ drivers/net/wireless/rtl818x/rtl8180/rtl8225.h | 23 + drivers/net/wireless/rtl818x/rtl8180/sa2400.c | 228 ++++ drivers/net/wireless/rtl818x/rtl8180/sa2400.h | 36 + drivers/net/wireless/rtl818x/rtl8180_dev.c | 1188 ------------------ drivers/net/wireless/rtl818x/rtl8180_grf5101.c | 190 --- drivers/net/wireless/rtl818x/rtl8180_grf5101.h | 28 - drivers/net/wireless/rtl818x/rtl8180_max2820.c | 169 --- drivers/net/wireless/rtl818x/rtl8180_max2820.h | 28 - drivers/net/wireless/rtl818x/rtl8180_rtl8225.c | 791 ------------ drivers/net/wireless/rtl818x/rtl8180_rtl8225.h | 23 - drivers/net/wireless/rtl818x/rtl8180_sa2400.c | 228 ---- drivers/net/wireless/rtl818x/rtl8180_sa2400.h | 36 - drivers/net/wireless/rtl818x/rtl8187.h | 271 ---- drivers/net/wireless/rtl818x/rtl8187/Makefile | 5 + drivers/net/wireless/rtl818x/rtl8187/dev.c | 1591 ++++++++++++++++++++++++ drivers/net/wireless/rtl818x/rtl8187/leds.c | 245 ++++ drivers/net/wireless/rtl818x/rtl8187/leds.h | 59 + drivers/net/wireless/rtl818x/rtl8187/rfkill.c | 64 + drivers/net/wireless/rtl818x/rtl8187/rfkill.h | 8 + drivers/net/wireless/rtl818x/rtl8187/rtl8187.h | 271 ++++ drivers/net/wireless/rtl818x/rtl8187/rtl8225.c | 961 ++++++++++++++ drivers/net/wireless/rtl818x/rtl8187/rtl8225.h | 44 + drivers/net/wireless/rtl818x/rtl8187_dev.c | 1591 ------------------------ drivers/net/wireless/rtl818x/rtl8187_leds.c | 245 ---- drivers/net/wireless/rtl818x/rtl8187_leds.h | 59 - drivers/net/wireless/rtl818x/rtl8187_rfkill.c | 64 - drivers/net/wireless/rtl818x/rtl8187_rfkill.h | 8 - drivers/net/wireless/rtl818x/rtl8187_rtl8225.c | 961 -------------- drivers/net/wireless/rtl818x/rtl8187_rtl8225.h | 44 - 39 files changed, 6066 insertions(+), 6061 deletions(-) delete mode 100644 drivers/net/wireless/rtl818x/rtl8180.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/Makefile create mode 100644 drivers/net/wireless/rtl818x/rtl8180/dev.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/grf5101.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/grf5101.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/max2820.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/max2820.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/rtl8180.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/rtl8225.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/rtl8225.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/sa2400.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/sa2400.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_dev.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_grf5101.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_grf5101.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_max2820.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_max2820.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_rtl8225.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_rtl8225.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_sa2400.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_sa2400.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/Makefile create mode 100644 drivers/net/wireless/rtl818x/rtl8187/dev.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/leds.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/leds.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rfkill.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rfkill.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rtl8187.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rtl8225.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rtl8225.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_dev.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_leds.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_leds.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rfkill.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rfkill.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rtl8225.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rtl8225.h (limited to 'drivers/net') diff --git a/drivers/net/wireless/rtl818x/Makefile b/drivers/net/wireless/rtl818x/Makefile index 93cbfbedb46d..997569076923 100644 --- a/drivers/net/wireless/rtl818x/Makefile +++ b/drivers/net/wireless/rtl818x/Makefile @@ -1,7 +1,2 @@ -rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o -rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o rtl8187_leds.o rtl8187_rfkill.o - -obj-$(CONFIG_RTL8180) += rtl8180.o -obj-$(CONFIG_RTL8187) += rtl8187.o - - +obj-$(CONFIG_RTL8180) += rtl8180/ +obj-$(CONFIG_RTL8187) += rtl8187/ diff --git a/drivers/net/wireless/rtl818x/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180.h deleted file mode 100644 index 30523314da43..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef RTL8180_H -#define RTL8180_H - -#include "rtl818x.h" - -#define MAX_RX_SIZE IEEE80211_MAX_RTS_THRESHOLD - -#define RF_PARAM_ANALOGPHY (1 << 0) -#define RF_PARAM_ANTBDEFAULT (1 << 1) -#define RF_PARAM_CARRIERSENSE1 (1 << 2) -#define RF_PARAM_CARRIERSENSE2 (1 << 3) - -#define BB_ANTATTEN_CHAN14 0x0C -#define BB_ANTENNA_B 0x40 - -#define BB_HOST_BANG (1 << 30) -#define BB_HOST_BANG_EN (1 << 2) -#define BB_HOST_BANG_CLK (1 << 1) -#define BB_HOST_BANG_DATA 1 - -#define ANAPARAM_TXDACOFF_SHIFT 27 -#define ANAPARAM_PWR0_SHIFT 28 -#define ANAPARAM_PWR0_MASK (0x07 << ANAPARAM_PWR0_SHIFT) -#define ANAPARAM_PWR1_SHIFT 20 -#define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) - -struct rtl8180_tx_desc { - __le32 flags; - __le16 rts_duration; - __le16 plcp_len; - __le32 tx_buf; - __le32 frame_len; - __le32 next_tx_desc; - u8 cw; - u8 retry_limit; - u8 agc; - u8 flags2; - u32 reserved[2]; -} __packed; - -struct rtl8180_rx_desc { - __le32 flags; - __le32 flags2; - union { - __le32 rx_buf; - __le64 tsft; - }; -} __packed; - -struct rtl8180_tx_ring { - struct rtl8180_tx_desc *desc; - dma_addr_t dma; - unsigned int idx; - unsigned int entries; - struct sk_buff_head queue; -}; - -struct rtl8180_vif { - struct ieee80211_hw *dev; - - /* beaconing */ - struct delayed_work beacon_work; - bool enable_beacon; -}; - -struct rtl8180_priv { - /* common between rtl818x drivers */ - struct rtl818x_csr __iomem *map; - const struct rtl818x_rf_ops *rf; - struct ieee80211_vif *vif; - - /* rtl8180 driver specific */ - spinlock_t lock; - struct rtl8180_rx_desc *rx_ring; - dma_addr_t rx_ring_dma; - unsigned int rx_idx; - struct sk_buff *rx_buf[32]; - struct rtl8180_tx_ring tx_ring[4]; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; - struct ieee80211_supported_band band; - struct pci_dev *pdev; - u32 rx_conf; - - int r8185; - u32 anaparam; - u16 rfparam; - u8 csthreshold; - - /* sequence # */ - u16 seqno; -}; - -void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); -void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam); - -static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr) -{ - return ioread8(addr); -} - -static inline u16 rtl818x_ioread16(struct rtl8180_priv *priv, __le16 __iomem *addr) -{ - return ioread16(addr); -} - -static inline u32 rtl818x_ioread32(struct rtl8180_priv *priv, __le32 __iomem *addr) -{ - return ioread32(addr); -} - -static inline void rtl818x_iowrite8(struct rtl8180_priv *priv, - u8 __iomem *addr, u8 val) -{ - iowrite8(val, addr); -} - -static inline void rtl818x_iowrite16(struct rtl8180_priv *priv, - __le16 __iomem *addr, u16 val) -{ - iowrite16(val, addr); -} - -static inline void rtl818x_iowrite32(struct rtl8180_priv *priv, - __le32 __iomem *addr, u32 val) -{ - iowrite32(val, addr); -} - -#endif /* RTL8180_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/Makefile b/drivers/net/wireless/rtl818x/rtl8180/Makefile new file mode 100644 index 000000000000..cb4fb8596f0b --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/Makefile @@ -0,0 +1,5 @@ +rtl8180-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o + +obj-$(CONFIG_RTL8180) += rtl8180.o + +ccflags-y += -Idrivers/net/wireless/rtl818x diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c new file mode 100644 index 000000000000..5851cbc1e957 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -0,0 +1,1188 @@ + +/* + * Linux device driver for RTL8180 / RTL8185 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello , et al. + * + * Thanks to Realtek for their support! + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "rtl8180.h" +#include "rtl8225.h" +#include "sa2400.h" +#include "max2820.h" +#include "grf5101.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); +MODULE_LICENSE("GPL"); + +static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { + /* rtl8185 */ + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, + { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) }, + { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) }, + + /* rtl8180 */ + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) }, + { PCI_DEVICE(0x1799, 0x6001) }, + { PCI_DEVICE(0x1799, 0x6020) }, + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, rtl8180_table); + +static const struct ieee80211_rate rtl818x_rates[] = { + { .bitrate = 10, .hw_value = 0, }, + { .bitrate = 20, .hw_value = 1, }, + { .bitrate = 55, .hw_value = 2, }, + { .bitrate = 110, .hw_value = 3, }, + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static const struct ieee80211_channel rtl818x_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + + +void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + int i = 10; + u32 buf; + + buf = (data << 8) | addr; + + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80); + while (i--) { + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf); + if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF)) + return; + } +} + +static void rtl8180_handle_rx(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + unsigned int count = 32; + u8 signal, agc, sq; + + while (count--) { + struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; + struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; + u32 flags = le32_to_cpu(entry->flags); + + if (flags & RTL818X_RX_DESC_FLAG_OWN) + return; + + if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | + RTL818X_RX_DESC_FLAG_FOF | + RTL818X_RX_DESC_FLAG_RX_ERR))) + goto done; + else { + u32 flags2 = le32_to_cpu(entry->flags2); + struct ieee80211_rx_status rx_status = {0}; + struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE); + + if (unlikely(!new_skb)) + goto done; + + pci_unmap_single(priv->pdev, + *((dma_addr_t *)skb->cb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + skb_put(skb, flags & 0xFFF); + + rx_status.antenna = (flags2 >> 15) & 1; + rx_status.rate_idx = (flags >> 20) & 0xF; + agc = (flags2 >> 17) & 0x7F; + if (priv->r8185) { + if (rx_status.rate_idx > 3) + signal = 90 - clamp_t(u8, agc, 25, 90); + else + signal = 95 - clamp_t(u8, agc, 30, 95); + } else { + sq = flags2 & 0xff; + signal = priv->rf->calc_rssi(agc, sq); + } + rx_status.signal = signal; + rx_status.freq = dev->conf.channel->center_freq; + rx_status.band = dev->conf.channel->band; + rx_status.mactime = le64_to_cpu(entry->tsft); + rx_status.flag |= RX_FLAG_TSFT; + if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; + + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); + + skb = new_skb; + priv->rx_buf[priv->rx_idx] = skb; + *((dma_addr_t *) skb->cb) = + pci_map_single(priv->pdev, skb_tail_pointer(skb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + } + + done: + entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); + entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | + MAX_RX_SIZE); + if (priv->rx_idx == 31) + entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); + priv->rx_idx = (priv->rx_idx + 1) % 32; + } +} + +static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; + + while (skb_queue_len(&ring->queue)) { + struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + u32 flags = le32_to_cpu(entry->flags); + + if (flags & RTL818X_TX_DESC_FLAG_OWN) + return; + + ring->idx = (ring->idx + 1) % ring->entries; + skb = __skb_dequeue(&ring->queue); + pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), + skb->len, PCI_DMA_TODEVICE); + + info = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(info); + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && + (flags & RTL818X_TX_DESC_FLAG_TX_OK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.rates[0].count = (flags & 0xFF) + 1; + info->status.rates[1].idx = -1; + + ieee80211_tx_status_irqsafe(dev, skb); + if (ring->entries - skb_queue_len(&ring->queue) == 2) + ieee80211_wake_queue(dev, prio); + } +} + +static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) +{ + struct ieee80211_hw *dev = dev_id; + struct rtl8180_priv *priv = dev->priv; + u16 reg; + + spin_lock(&priv->lock); + reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS); + if (unlikely(reg == 0xFFFF)) { + spin_unlock(&priv->lock); + return IRQ_HANDLED; + } + + rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg); + + if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR)) + rtl8180_handle_tx(dev, 3); + + if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR)) + rtl8180_handle_tx(dev, 2); + + if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR)) + rtl8180_handle_tx(dev, 1); + + if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR)) + rtl8180_handle_tx(dev, 0); + + if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR)) + rtl8180_handle_rx(dev); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_ring *ring; + struct rtl8180_tx_desc *entry; + unsigned long flags; + unsigned int idx, prio; + dma_addr_t mapping; + u32 tx_flags; + u8 rc_flags; + u16 plcp_len = 0; + __le16 rts_duration = 0; + + prio = skb_get_queue_mapping(skb); + ring = &priv->tx_ring[prio]; + + mapping = pci_map_single(priv->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE); + + tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | + RTL818X_TX_DESC_FLAG_LS | + (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | + skb->len; + + if (priv->r8185) + tx_flags |= RTL818X_TX_DESC_FLAG_DMA | + RTL818X_TX_DESC_FLAG_NO_ENC; + + rc_flags = info->control.rates[0].flags; + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + tx_flags |= RTL818X_TX_DESC_FLAG_RTS; + tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + tx_flags |= RTL818X_TX_DESC_FLAG_CTS; + tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + } + + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) + rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, + info); + + if (!priv->r8185) { + unsigned int remainder; + + plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), + (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); + remainder = (16 * (skb->len + 4)) % + ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); + if (remainder <= 6) + plcp_len |= 1 << 15; + } + + spin_lock_irqsave(&priv->lock, flags); + + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + priv->seqno += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(priv->seqno); + } + + idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; + entry = &ring->desc[idx]; + + entry->rts_duration = rts_duration; + entry->plcp_len = cpu_to_le16(plcp_len); + entry->tx_buf = cpu_to_le32(mapping); + entry->frame_len = cpu_to_le32(skb->len); + entry->flags2 = info->control.rates[1].idx >= 0 ? + ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; + entry->retry_limit = info->control.rates[0].count; + entry->flags = cpu_to_le32(tx_flags); + __skb_queue_tail(&ring->queue, skb); + if (ring->entries - skb_queue_len(&ring->queue) < 2) + ieee80211_stop_queue(dev, prio); + + spin_unlock_irqrestore(&priv->lock, flags); + + rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); + + return 0; +} + +void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) +{ + u8 reg; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); +} + +static int rtl8180_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg; + + rtl818x_iowrite8(priv, &priv->map->CMD, 0); + rtl818x_ioread8(priv, &priv->map->CMD); + msleep(10); + + /* reset */ + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + rtl818x_ioread8(priv, &priv->map->CMD); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= (1 << 1); + reg |= RTL818X_CMD_RESET; + rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET); + rtl818x_ioread8(priv, &priv->map->CMD); + msleep(200); + + /* check success of reset */ + if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) { + wiphy_err(dev->wiphy, "reset timeout!\n"); + return -ETIMEDOUT; + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); + rtl818x_ioread8(priv, &priv->map->CMD); + msleep(200); + + if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) { + /* For cardbus */ + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + reg |= 1 << 1; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); + reg = rtl818x_ioread16(priv, &priv->map->FEMR); + reg |= (1 << 15) | (1 << 14) | (1 << 4); + rtl818x_iowrite16(priv, &priv->map->FEMR, reg); + } + + rtl818x_iowrite8(priv, &priv->map->MSR, 0); + + if (!priv->r8185) + rtl8180_set_anaparam(priv, priv->anaparam); + + rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); + rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); + rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); + rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); + rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); + + /* TODO: necessary? specs indicate not */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); + rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3)); + if (priv->r8185) { + reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); + rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4)); + } + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */ + + /* TODO: turn off hw wep on rtl8180 */ + + rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); + + if (priv->r8185) { + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); + rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); + + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + + /* TODO: set ClkRun enable? necessary? */ + reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6)); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + } else { + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1); + rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); + + rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); + rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); + } + + priv->rf->init(dev); + if (priv->r8185) + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + return 0; +} + +static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_rx_desc *entry; + int i; + + priv->rx_ring = pci_alloc_consistent(priv->pdev, + sizeof(*priv->rx_ring) * 32, + &priv->rx_ring_dma); + + if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) { + wiphy_err(dev->wiphy, "Cannot allocate RX ring\n"); + return -ENOMEM; + } + + memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32); + priv->rx_idx = 0; + + for (i = 0; i < 32; i++) { + struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE); + dma_addr_t *mapping; + entry = &priv->rx_ring[i]; + if (!skb) + return 0; + + priv->rx_buf[i] = skb; + mapping = (dma_addr_t *)skb->cb; + *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + entry->rx_buf = cpu_to_le32(*mapping); + entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | + MAX_RX_SIZE); + } + entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); + return 0; +} + +static void rtl8180_free_rx_ring(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + for (i = 0; i < 32; i++) { + struct sk_buff *skb = priv->rx_buf[i]; + if (!skb) + continue; + + pci_unmap_single(priv->pdev, + *((dma_addr_t *)skb->cb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + kfree_skb(skb); + } + + pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, + priv->rx_ring, priv->rx_ring_dma); + priv->rx_ring = NULL; +} + +static int rtl8180_init_tx_ring(struct ieee80211_hw *dev, + unsigned int prio, unsigned int entries) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_desc *ring; + dma_addr_t dma; + int i; + + ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma); + if (!ring || (unsigned long)ring & 0xFF) { + wiphy_err(dev->wiphy, "Cannot allocate TX ring (prio = %d)\n", + prio); + return -ENOMEM; + } + + memset(ring, 0, sizeof(*ring)*entries); + priv->tx_ring[prio].desc = ring; + priv->tx_ring[prio].dma = dma; + priv->tx_ring[prio].idx = 0; + priv->tx_ring[prio].entries = entries; + skb_queue_head_init(&priv->tx_ring[prio].queue); + + for (i = 0; i < entries; i++) + ring[i].next_tx_desc = + cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring)); + + return 0; +} + +static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; + + while (skb_queue_len(&ring->queue)) { + struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb = __skb_dequeue(&ring->queue); + + pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), + skb->len, PCI_DMA_TODEVICE); + kfree_skb(skb); + ring->idx = (ring->idx + 1) % ring->entries; + } + + pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries, + ring->desc, ring->dma); + ring->desc = NULL; +} + +static int rtl8180_start(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int ret, i; + u32 reg; + + ret = rtl8180_init_rx_ring(dev); + if (ret) + return ret; + + for (i = 0; i < 4; i++) + if ((ret = rtl8180_init_tx_ring(dev, i, 16))) + goto err_free_rings; + + ret = rtl8180_init_hw(dev); + if (ret) + goto err_free_rings; + + rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); + rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); + rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); + rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); + rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); + + ret = request_irq(priv->pdev->irq, rtl8180_interrupt, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) { + wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); + goto err_free_rings; + } + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + + rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); + rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); + + reg = RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_DATA | + (7 << 8 /* MAX RX DMA */) | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_NICMAC; + + if (priv->r8185) + reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2; + else { + reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) + ? RTL818X_RX_CONF_CSDM1 : 0; + reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2) + ? RTL818X_RX_CONF_CSDM2 : 0; + } + + priv->rx_conf = reg; + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + if (priv->r8185) { + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + /* disable early TX */ + rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f); + } + + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + reg |= (6 << 21 /* MAX TX DMA */) | + RTL818X_TX_CONF_NO_ICV; + + if (priv->r8185) + reg &= ~RTL818X_TX_CONF_PROBE_DTS; + else + reg &= ~RTL818X_TX_CONF_HW_SEQNUM; + + /* different meaning, same value on both rtl8185 and rtl8180 */ + reg &= ~RTL818X_TX_CONF_SAT_HWPLCP; + + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg |= RTL818X_CMD_RX_ENABLE; + reg |= RTL818X_CMD_TX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + return 0; + + err_free_rings: + rtl8180_free_rx_ring(dev); + for (i = 0; i < 4; i++) + if (priv->tx_ring[i].desc) + rtl8180_free_tx_ring(dev, i); + + return ret; +} + +static void rtl8180_stop(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u8 reg; + int i; + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= ~RTL818X_CMD_TX_ENABLE; + reg &= ~RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + priv->rf->stop(dev); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); + rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + free_irq(priv->pdev->irq, dev); + + rtl8180_free_rx_ring(dev); + for (i = 0; i < 4; i++) + rtl8180_free_tx_ring(dev, i); +} + +static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | + (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; +} + +static void rtl8180_beacon_work(struct work_struct *work) +{ + struct rtl8180_vif *vif_priv = + container_of(work, struct rtl8180_vif, beacon_work.work); + struct ieee80211_vif *vif = + container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); + struct ieee80211_hw *dev = vif_priv->dev; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + int err = 0; + + /* don't overflow the tx ring */ + if (ieee80211_queue_stopped(dev, 0)) + goto resched; + + /* grab a fresh beacon */ + skb = ieee80211_beacon_get(dev, vif); + if (!skb) + goto resched; + + /* + * update beacon timestamp w/ TSF value + * TODO: make hardware update beacon timestamp + */ + mgmt = (struct ieee80211_mgmt *)skb->data; + mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev)); + + /* TODO: use actual beacon queue */ + skb_set_queue_mapping(skb, 0); + + err = rtl8180_tx(dev, skb); + WARN_ON(err); + +resched: + /* + * schedule next beacon + * TODO: use hardware support for beacon timing + */ + schedule_delayed_work(&vif_priv->beacon_work, + usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); +} + +static int rtl8180_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_vif *vif_priv; + + /* + * We only support one active interface at a time. + */ + if (priv->vif) + return -EBUSY; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + break; + default: + return -EOPNOTSUPP; + } + + priv->vif = vif; + + /* Initialize driver private area */ + vif_priv = (struct rtl8180_vif *)&vif->drv_priv; + vif_priv->dev = dev; + INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8180_beacon_work); + vif_priv->enable_beacon = false; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], + le32_to_cpu(*(__le32 *)vif->addr)); + rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], + le16_to_cpu(*(__le16 *)(vif->addr + 4))); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + return 0; +} + +static void rtl8180_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8180_priv *priv = dev->priv; + priv->vif = NULL; +} + +static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) +{ + struct rtl8180_priv *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + priv->rf->set_chan(dev, conf); + + return 0; +} + +static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_vif *vif_priv; + int i; + u8 reg; + + vif_priv = (struct rtl8180_vif *)&vif->drv_priv; + + if (changed & BSS_CHANGED_BSSID) { + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], + info->bssid[i]); + + if (is_valid_ether_addr(info->bssid)) { + if (vif->type == NL80211_IFTYPE_ADHOC) + reg = RTL818X_MSR_ADHOC; + else + reg = RTL818X_MSR_INFRA; + } else + reg = RTL818X_MSR_NO_LINK; + rtl818x_iowrite8(priv, &priv->map->MSR, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) + priv->rf->conf_erp(dev, info); + + if (changed & BSS_CHANGED_BEACON_ENABLED) + vif_priv->enable_beacon = info->enable_beacon; + + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { + cancel_delayed_work_sync(&vif_priv->beacon_work); + if (vif_priv->enable_beacon) + schedule_work(&vif_priv->beacon_work.work); + } +} + +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, + struct netdev_hw_addr_list *mc_list) +{ + return netdev_hw_addr_list_count(mc_list); +} + +static void rtl8180_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct rtl8180_priv *priv = dev->priv; + + if (changed_flags & FIF_FCSFAIL) + priv->rx_conf ^= RTL818X_RX_CONF_FCS; + if (changed_flags & FIF_CONTROL) + priv->rx_conf ^= RTL818X_RX_CONF_CTRL; + if (changed_flags & FIF_OTHER_BSS) + priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; + if (*total_flags & FIF_ALLMULTI || multicast > 0) + priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; + else + priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; + + *total_flags = 0; + + if (priv->rx_conf & RTL818X_RX_CONF_FCS) + *total_flags |= FIF_FCSFAIL; + if (priv->rx_conf & RTL818X_RX_CONF_CTRL) + *total_flags |= FIF_CONTROL; + if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) + *total_flags |= FIF_OTHER_BSS; + if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) + *total_flags |= FIF_ALLMULTI; + + rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf); +} + +static const struct ieee80211_ops rtl8180_ops = { + .tx = rtl8180_tx, + .start = rtl8180_start, + .stop = rtl8180_stop, + .add_interface = rtl8180_add_interface, + .remove_interface = rtl8180_remove_interface, + .config = rtl8180_config, + .bss_info_changed = rtl8180_bss_info_changed, + .prepare_multicast = rtl8180_prepare_multicast, + .configure_filter = rtl8180_configure_filter, + .get_tsf = rtl8180_get_tsf, +}; + +static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8180_priv *priv = dev->priv; + u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + + eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; + eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; + eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; + eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; +} + +static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8180_priv *priv = dev->priv; + u8 reg = 2 << 6; + + if (eeprom->reg_data_in) + reg |= RTL818X_EEPROM_CMD_WRITE; + if (eeprom->reg_data_out) + reg |= RTL818X_EEPROM_CMD_READ; + if (eeprom->reg_data_clock) + reg |= RTL818X_EEPROM_CMD_CK; + if (eeprom->reg_chip_select) + reg |= RTL818X_EEPROM_CMD_CS; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); +} + +static int __devinit rtl8180_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *dev; + struct rtl8180_priv *priv; + unsigned long mem_addr, mem_len; + unsigned int io_addr, io_len; + int err, i; + struct eeprom_93cx6 eeprom; + const char *chip_name, *rf_name = NULL; + u32 reg; + u16 eeprom_val; + u8 mac_addr[ETH_ALEN]; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n", + pci_name(pdev)); + return err; + } + + err = pci_request_regions(pdev, KBUILD_MODNAME); + if (err) { + printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n", + pci_name(pdev)); + return err; + } + + io_addr = pci_resource_start(pdev, 0); + io_len = pci_resource_len(pdev, 0); + mem_addr = pci_resource_start(pdev, 1); + mem_len = pci_resource_len(pdev, 1); + + if (mem_len < sizeof(struct rtl818x_csr) || + io_len < sizeof(struct rtl818x_csr)) { + printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + + if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) || + (err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))) { + printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n", + pci_name(pdev)); + goto err_free_reg; + } + + pci_set_master(pdev); + + dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops); + if (!dev) { + printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + + priv = dev->priv; + priv->pdev = pdev; + + dev->max_rates = 2; + SET_IEEE80211_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + + priv->map = pci_iomap(pdev, 1, mem_len); + if (!priv->map) + priv->map = pci_iomap(pdev, 0, io_len); + + if (!priv->map) { + printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n", + pci_name(pdev)); + goto err_free_dev; + } + + BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); + BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); + + memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); + memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); + + priv->band.band = IEEE80211_BAND_2GHZ; + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = 4; + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_SIGNAL_UNSPEC; + dev->vif_data_size = sizeof(struct rtl8180_vif); + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + dev->queues = 1; + dev->max_signal = 65; + + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + reg &= RTL818X_TX_CONF_HWVER_MASK; + switch (reg) { + case RTL818X_TX_CONF_R8180_ABCD: + chip_name = "RTL8180"; + break; + case RTL818X_TX_CONF_R8180_F: + chip_name = "RTL8180vF"; + break; + case RTL818X_TX_CONF_R8185_ABC: + chip_name = "RTL8185"; + break; + case RTL818X_TX_CONF_R8185_D: + chip_name = "RTL8185vD"; + break; + default: + printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", + pci_name(pdev), reg >> 25); + goto err_iounmap; + } + + priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; + if (priv->r8185) { + priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); + pci_try_set_mwi(pdev); + } + + eeprom.data = dev; + eeprom.register_read = rtl8180_eeprom_register_read; + eeprom.register_write = rtl8180_eeprom_register_write; + if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) + eeprom.width = PCI_EEPROM_WIDTH_93C66; + else + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); + eeprom_val &= 0xFF; + switch (eeprom_val) { + case 1: rf_name = "Intersil"; + break; + case 2: rf_name = "RFMD"; + break; + case 3: priv->rf = &sa2400_rf_ops; + break; + case 4: priv->rf = &max2820_rf_ops; + break; + case 5: priv->rf = &grf5101_rf_ops; + break; + case 9: priv->rf = rtl8180_detect_rf(dev); + break; + case 10: + rf_name = "RTL8255"; + break; + default: + printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", + pci_name(pdev), eeprom_val); + goto err_iounmap; + } + + if (!priv->rf) { + printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n", + pci_name(pdev), rf_name); + goto err_iounmap; + } + + eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); + priv->csthreshold = eeprom_val >> 8; + if (!priv->r8185) { + __le32 anaparam; + eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); + priv->anaparam = le32_to_cpu(anaparam); + eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); + } + + eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3); + if (!is_valid_ether_addr(mac_addr)) { + printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" + " randomly generated MAC addr\n", pci_name(pdev)); + random_ether_addr(mac_addr); + } + SET_IEEE80211_PERM_ADDR(dev, mac_addr); + + /* CCK TX power */ + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); + priv->channels[i].hw_value = txpwr & 0xFF; + priv->channels[i + 1].hw_value = txpwr >> 8; + } + + /* OFDM TX power */ + if (priv->r8185) { + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); + priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; + priv->channels[i + 1].hw_value |= txpwr & 0xFF00; + } + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + spin_lock_init(&priv->lock); + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "%s (rtl8180): Cannot register device\n", + pci_name(pdev)); + goto err_iounmap; + } + + wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n", + mac_addr, chip_name, priv->rf->name); + + return 0; + + err_iounmap: + iounmap(priv->map); + + 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; +} + +static void __devexit rtl8180_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct rtl8180_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + + priv = dev->priv; + + pci_iounmap(pdev, priv->map); + pci_release_regions(pdev); + pci_disable_device(pdev); + ieee80211_free_hw(dev); +} + +#ifdef CONFIG_PM +static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int rtl8180_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver rtl8180_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8180_table, + .probe = rtl8180_probe, + .remove = __devexit_p(rtl8180_remove), +#ifdef CONFIG_PM + .suspend = rtl8180_suspend, + .resume = rtl8180_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rtl8180_init(void) +{ + return pci_register_driver(&rtl8180_driver); +} + +static void __exit rtl8180_exit(void) +{ + pci_unregister_driver(&rtl8180_driver); +} + +module_init(rtl8180_init); +module_exit(rtl8180_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.c b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c new file mode 100644 index 000000000000..5ee7589dd546 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c @@ -0,0 +1,190 @@ + +/* + * Radio tuning for GCT GRF5101 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "grf5101.h" + +static const int grf5101_encode[] = { + 0x0, 0x8, 0x4, 0xC, + 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, + 0x3, 0xB, 0x7, 0xF +}; + +static void write_grf5101(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + u32 phy_config; + + phy_config = grf5101_encode[(data >> 8) & 0xF]; + phy_config |= grf5101_encode[(data >> 4) & 0xF] << 4; + phy_config |= grf5101_encode[data & 0xF] << 8; + phy_config |= grf5101_encode[(addr >> 1) & 0xF] << 12; + phy_config |= (addr & 1) << 16; + phy_config |= grf5101_encode[(data & 0xf000) >> 12] << 24; + + /* MAC will bang bits to the chip */ + phy_config |= 0x90000000; + + rtl818x_iowrite32(priv, + (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); + + msleep(3); +} + +static void grf5101_write_phy_antenna(struct ieee80211_hw *dev, short chan) +{ + struct rtl8180_priv *priv = dev->priv; + u8 ant = GRF5101_ANTENNA; + + if (priv->rfparam & RF_PARAM_ANTBDEFAULT) + ant |= BB_ANTENNA_B; + + if (chan == 14) + ant |= BB_ANTATTEN_CHAN14; + + rtl8180_write_phy(dev, 0x10, ant); +} + +static u8 grf5101_rf_calc_rssi(u8 agc, u8 sq) +{ + if (agc > 60) + return 65; + + /* TODO(?): just return agc (or agc + 5) to avoid mult / div */ + return 65 * agc / 60; +} + +static void grf5101_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; + u32 chan = channel - 1; + + /* set TX power */ + write_grf5101(dev, 0x15, 0x0); + write_grf5101(dev, 0x06, txpw); + write_grf5101(dev, 0x15, 0x10); + write_grf5101(dev, 0x15, 0x0); + + /* set frequency */ + write_grf5101(dev, 0x07, 0x0); + write_grf5101(dev, 0x0B, chan); + write_grf5101(dev, 0x07, 0x1000); + + grf5101_write_phy_antenna(dev, channel); +} + +static void grf5101_rf_stop(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u32 anaparam; + + anaparam = priv->anaparam; + anaparam &= 0x000fffff; + anaparam |= 0x3f900000; + rtl8180_set_anaparam(priv, anaparam); + + write_grf5101(dev, 0x07, 0x0); + write_grf5101(dev, 0x1f, 0x45); + write_grf5101(dev, 0x1f, 0x5); + write_grf5101(dev, 0x00, 0x8e4); +} + +static void grf5101_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + rtl8180_set_anaparam(priv, priv->anaparam); + + write_grf5101(dev, 0x1f, 0x0); + write_grf5101(dev, 0x1f, 0x0); + write_grf5101(dev, 0x1f, 0x40); + write_grf5101(dev, 0x1f, 0x60); + write_grf5101(dev, 0x1f, 0x61); + write_grf5101(dev, 0x1f, 0x61); + write_grf5101(dev, 0x00, 0xae4); + write_grf5101(dev, 0x1f, 0x1); + write_grf5101(dev, 0x1f, 0x41); + write_grf5101(dev, 0x1f, 0x61); + + write_grf5101(dev, 0x01, 0x1a23); + write_grf5101(dev, 0x02, 0x4971); + write_grf5101(dev, 0x03, 0x41de); + write_grf5101(dev, 0x04, 0x2d80); + write_grf5101(dev, 0x05, 0x68ff); /* 0x61ff original value */ + write_grf5101(dev, 0x06, 0x0); + write_grf5101(dev, 0x07, 0x0); + write_grf5101(dev, 0x08, 0x7533); + write_grf5101(dev, 0x09, 0xc401); + write_grf5101(dev, 0x0a, 0x0); + write_grf5101(dev, 0x0c, 0x1c7); + write_grf5101(dev, 0x0d, 0x29d3); + write_grf5101(dev, 0x0e, 0x2e8); + write_grf5101(dev, 0x10, 0x192); + write_grf5101(dev, 0x11, 0x248); + write_grf5101(dev, 0x12, 0x0); + write_grf5101(dev, 0x13, 0x20c4); + write_grf5101(dev, 0x14, 0xf4fc); + write_grf5101(dev, 0x15, 0x0); + write_grf5101(dev, 0x16, 0x1500); + + write_grf5101(dev, 0x07, 0x1000); + + /* baseband configuration */ + rtl8180_write_phy(dev, 0, 0xa8); + rtl8180_write_phy(dev, 3, 0x0); + rtl8180_write_phy(dev, 4, 0xc0); + rtl8180_write_phy(dev, 5, 0x90); + rtl8180_write_phy(dev, 6, 0x1e); + rtl8180_write_phy(dev, 7, 0x64); + + grf5101_write_phy_antenna(dev, 1); + + rtl8180_write_phy(dev, 0x11, 0x88); + + if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & + RTL818X_CONFIG2_ANTENNA_DIV) + rtl8180_write_phy(dev, 0x12, 0xc0); /* enable ant diversity */ + else + rtl8180_write_phy(dev, 0x12, 0x40); /* disable ant diversity */ + + rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); + + rtl8180_write_phy(dev, 0x19, 0x0); + rtl8180_write_phy(dev, 0x1a, 0xa0); + rtl8180_write_phy(dev, 0x1b, 0x44); +} + +const struct rtl818x_rf_ops grf5101_rf_ops = { + .name = "GCT", + .init = grf5101_rf_init, + .stop = grf5101_rf_stop, + .set_chan = grf5101_rf_set_channel, + .calc_rssi = grf5101_rf_calc_rssi, +}; diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.h b/drivers/net/wireless/rtl818x/rtl8180/grf5101.h new file mode 100644 index 000000000000..76647111bcff --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.h @@ -0,0 +1,28 @@ +#ifndef RTL8180_GRF5101_H +#define RTL8180_GRF5101_H + +/* + * Radio tuning for GCT GRF5101 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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. + */ + +#define GRF5101_ANTENNA 0xA3 + +extern const struct rtl818x_rf_ops grf5101_rf_ops; + +#endif /* RTL8180_GRF5101_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.c b/drivers/net/wireless/rtl818x/rtl8180/max2820.c new file mode 100644 index 000000000000..667b3363d437 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.c @@ -0,0 +1,169 @@ +/* + * Radio tuning for Maxim max2820 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "max2820.h" + +static const u32 max2820_chan[] = { + 12, /* CH 1 */ + 17, + 22, + 27, + 32, + 37, + 42, + 47, + 52, + 57, + 62, + 67, + 72, + 84, /* CH 14 */ +}; + +static void write_max2820(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + u32 phy_config; + + phy_config = 0x90 + (data & 0xf); + phy_config <<= 16; + phy_config += addr; + phy_config <<= 8; + phy_config += (data >> 4) & 0xff; + + rtl818x_iowrite32(priv, + (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); + + msleep(1); +} + +static void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan) +{ + struct rtl8180_priv *priv = dev->priv; + u8 ant; + + ant = MAXIM_ANTENNA; + if (priv->rfparam & RF_PARAM_ANTBDEFAULT) + ant |= BB_ANTENNA_B; + if (chan == 14) + ant |= BB_ANTATTEN_CHAN14; + + rtl8180_write_phy(dev, 0x10, ant); +} + +static u8 max2820_rf_calc_rssi(u8 agc, u8 sq) +{ + bool odd; + + odd = !!(agc & 1); + + agc >>= 1; + if (odd) + agc += 76; + else + agc += 66; + + /* TODO: change addends above to avoid mult / div below */ + return 65 * agc / 100; +} + +static void max2820_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int channel = conf ? + ieee80211_frequency_to_channel(conf->channel->center_freq) : 1; + unsigned int chan_idx = channel - 1; + u32 txpw = priv->channels[chan_idx].hw_value & 0xFF; + u32 chan = max2820_chan[chan_idx]; + + /* While philips SA2400 drive the PA bias from + * sa2400, for MAXIM we do this directly from BB */ + rtl8180_write_phy(dev, 3, txpw); + + max2820_write_phy_antenna(dev, channel); + write_max2820(dev, 3, chan); +} + +static void max2820_rf_stop(struct ieee80211_hw *dev) +{ + rtl8180_write_phy(dev, 3, 0x8); + write_max2820(dev, 1, 0); +} + + +static void max2820_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + /* MAXIM from netbsd driver */ + write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */ + write_max2820(dev, 1, 0x01e); /* enable register */ + write_max2820(dev, 2, 0x001); /* synt register */ + + max2820_rf_set_channel(dev, NULL); + + write_max2820(dev, 4, 0x313); /* rx register */ + + /* PA is driven directly by the BB, we keep the MAXIM bias + * at the highest value in case that setting it to lower + * values may introduce some further attenuation somewhere.. + */ + write_max2820(dev, 5, 0x00f); + + /* baseband configuration */ + rtl8180_write_phy(dev, 0, 0x88); /* sys1 */ + rtl8180_write_phy(dev, 3, 0x08); /* txagc */ + rtl8180_write_phy(dev, 4, 0xf8); /* lnadet */ + rtl8180_write_phy(dev, 5, 0x90); /* ifagcinit */ + rtl8180_write_phy(dev, 6, 0x1a); /* ifagclimit */ + rtl8180_write_phy(dev, 7, 0x64); /* ifagcdet */ + + max2820_write_phy_antenna(dev, 1); + + rtl8180_write_phy(dev, 0x11, 0x88); /* trl */ + + if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & + RTL818X_CONFIG2_ANTENNA_DIV) + rtl8180_write_phy(dev, 0x12, 0xc7); + else + rtl8180_write_phy(dev, 0x12, 0x47); + + rtl8180_write_phy(dev, 0x13, 0x9b); + + rtl8180_write_phy(dev, 0x19, 0x0); /* CHESTLIM */ + rtl8180_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM */ + + max2820_rf_set_channel(dev, NULL); +} + +const struct rtl818x_rf_ops max2820_rf_ops = { + .name = "Maxim", + .init = max2820_rf_init, + .stop = max2820_rf_stop, + .set_chan = max2820_rf_set_channel, + .calc_rssi = max2820_rf_calc_rssi, +}; diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.h b/drivers/net/wireless/rtl818x/rtl8180/max2820.h new file mode 100644 index 000000000000..61cf6d1e7d57 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.h @@ -0,0 +1,28 @@ +#ifndef RTL8180_MAX2820_H +#define RTL8180_MAX2820_H + +/* + * Radio tuning for Maxim max2820 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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. + */ + +#define MAXIM_ANTENNA 0xb3 + +extern const struct rtl818x_rf_ops max2820_rf_ops; + +#endif /* RTL8180_MAX2820_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h new file mode 100644 index 000000000000..30523314da43 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h @@ -0,0 +1,130 @@ +#ifndef RTL8180_H +#define RTL8180_H + +#include "rtl818x.h" + +#define MAX_RX_SIZE IEEE80211_MAX_RTS_THRESHOLD + +#define RF_PARAM_ANALOGPHY (1 << 0) +#define RF_PARAM_ANTBDEFAULT (1 << 1) +#define RF_PARAM_CARRIERSENSE1 (1 << 2) +#define RF_PARAM_CARRIERSENSE2 (1 << 3) + +#define BB_ANTATTEN_CHAN14 0x0C +#define BB_ANTENNA_B 0x40 + +#define BB_HOST_BANG (1 << 30) +#define BB_HOST_BANG_EN (1 << 2) +#define BB_HOST_BANG_CLK (1 << 1) +#define BB_HOST_BANG_DATA 1 + +#define ANAPARAM_TXDACOFF_SHIFT 27 +#define ANAPARAM_PWR0_SHIFT 28 +#define ANAPARAM_PWR0_MASK (0x07 << ANAPARAM_PWR0_SHIFT) +#define ANAPARAM_PWR1_SHIFT 20 +#define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) + +struct rtl8180_tx_desc { + __le32 flags; + __le16 rts_duration; + __le16 plcp_len; + __le32 tx_buf; + __le32 frame_len; + __le32 next_tx_desc; + u8 cw; + u8 retry_limit; + u8 agc; + u8 flags2; + u32 reserved[2]; +} __packed; + +struct rtl8180_rx_desc { + __le32 flags; + __le32 flags2; + union { + __le32 rx_buf; + __le64 tsft; + }; +} __packed; + +struct rtl8180_tx_ring { + struct rtl8180_tx_desc *desc; + dma_addr_t dma; + unsigned int idx; + unsigned int entries; + struct sk_buff_head queue; +}; + +struct rtl8180_vif { + struct ieee80211_hw *dev; + + /* beaconing */ + struct delayed_work beacon_work; + bool enable_beacon; +}; + +struct rtl8180_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr __iomem *map; + const struct rtl818x_rf_ops *rf; + struct ieee80211_vif *vif; + + /* rtl8180 driver specific */ + spinlock_t lock; + struct rtl8180_rx_desc *rx_ring; + dma_addr_t rx_ring_dma; + unsigned int rx_idx; + struct sk_buff *rx_buf[32]; + struct rtl8180_tx_ring tx_ring[4]; + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct pci_dev *pdev; + u32 rx_conf; + + int r8185; + u32 anaparam; + u16 rfparam; + u8 csthreshold; + + /* sequence # */ + u16 seqno; +}; + +void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); +void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam); + +static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr) +{ + return ioread8(addr); +} + +static inline u16 rtl818x_ioread16(struct rtl8180_priv *priv, __le16 __iomem *addr) +{ + return ioread16(addr); +} + +static inline u32 rtl818x_ioread32(struct rtl8180_priv *priv, __le32 __iomem *addr) +{ + return ioread32(addr); +} + +static inline void rtl818x_iowrite8(struct rtl8180_priv *priv, + u8 __iomem *addr, u8 val) +{ + iowrite8(val, addr); +} + +static inline void rtl818x_iowrite16(struct rtl8180_priv *priv, + __le16 __iomem *addr, u16 val) +{ + iowrite16(val, addr); +} + +static inline void rtl818x_iowrite32(struct rtl8180_priv *priv, + __le32 __iomem *addr, u32 val) +{ + iowrite32(val, addr); +} + +#endif /* RTL8180_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c new file mode 100644 index 000000000000..7c4574ba9d75 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c @@ -0,0 +1,791 @@ + +/* + * Radio tuning for RTL8225 on RTL8180 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8180 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * Thanks to Realtek for their support! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "rtl8225.h" + +static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg80, reg84, reg82; + u32 bangdata; + int i; + + bangdata = (data << 4) | (addr & 0xf); + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); + + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + for (i = 15; i >= 0; i--) { + u16 reg = reg80; + + if (bangdata & (1 << i)) + reg |= 1; + + if (i & 1) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + + if (!(i & 1)) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); +} + +static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg80, reg82, reg84, out; + int i; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400; + + reg80 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(4); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(5); + + for (i = 4; i >= 0; i--) { + u16 reg = reg80 | ((addr >> i) & 1); + + if (!(i & 1)) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(1); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + if (i & 1) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(1); + } + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + out = 0; + for (i = 11; i >= 0; i--) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(1); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) + out |= 1 << i; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); + + return out; +} + +static const u16 rtl8225bcd_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static const u8 rtl8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, + 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, + 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, + 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, + 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, + 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, + 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static const u8 rtl8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ +}; + +static const u8 rtl8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd +}; + +static const u8 rtl8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static const u8 rtl8225_tx_power_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static const u8 rtl8225_tx_power_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225_tx_power_ofdm[] = { + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static const u32 rtl8225_chan[] = { + 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, + 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 +}; + +static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8180_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xFF; + ofdm_power = priv->channels[channel - 1].hw_value >> 8; + + cck_power = min(cck_power, (u8)35); + ofdm_power = min(ofdm_power, (u8)35); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); + + if (channel == 14) + tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; + else + tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + msleep(1); /* FIXME: optional? */ + + /* anaparam2 on */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1); + + tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; + + rtl8225_write_phy_ofdm(dev, 5, *tmp); + rtl8225_write_phy_ofdm(dev, 7, *tmp); + + msleep(1); +} + +static void rtl8225_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); + + /* host_pci_init */ + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + msleep(200); /* FIXME: ehh?? */ + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); + + /* TODO: check if we need really to change BRSR to do RF config */ + rtl818x_ioread16(priv, &priv->map->BRSR); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write(dev, 0x0, 0x067); + rtl8225_write(dev, 0x1, 0xFE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x8BE); + rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */ + rtl8225_write(dev, 0x6, 0xAE6); + rtl8225_write(dev, 0x7, rtl8225_chan[0]); + rtl8225_write(dev, 0x8, 0x01F); + rtl8225_write(dev, 0x9, 0x334); + rtl8225_write(dev, 0xA, 0xFD4); + rtl8225_write(dev, 0xB, 0x391); + rtl8225_write(dev, 0xC, 0x050); + rtl8225_write(dev, 0xD, 0x6DB); + rtl8225_write(dev, 0xE, 0x029); + rtl8225_write(dev, 0xF, 0x914); msleep(1); + + rtl8225_write(dev, 0x2, 0xC4D); msleep(100); + + rtl8225_write(dev, 0x0, 0x127); + + for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x027); + rtl8225_write(dev, 0x0, 0x22F); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + msleep(1); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + msleep(1); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); + rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); + rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); + rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); + rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); + rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); + rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1); + rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); + rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); + rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1); + rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1); + rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1); + rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1); + rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1); + rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1); + rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1); + rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); + + rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1); + + rtl8225_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + rtl8225_write(dev, 0x0c, 0x50); + /* set OFDM initial gain */ + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]); + /* set CCK threshold */ + rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]); +} + +static const u8 rtl8225z2_tx_power_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225z2_tx_power_cck_B[] = { + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04 +}; + +static const u8 rtl8225z2_tx_power_cck_A[] = { + 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04 +}; + +static const u8 rtl8225z2_tx_power_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8180_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xFF; + ofdm_power = priv->channels[channel - 1].hw_value >> 8; + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else if (cck_power == 12) + tmp = rtl8225z2_tx_power_cck_B; + else if (cck_power == 13) + tmp = rtl8225z2_tx_power_cck_A; + else + tmp = rtl8225z2_tx_power_cck; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + cck_power = min(cck_power, (u8)35); + if (cck_power == 13 || cck_power == 14) + cck_power = 12; + if (cck_power >= 15) + cck_power -= 2; + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power); + rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK); + msleep(1); + + ofdm_power = min(ofdm_power, (u8)35); + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power); + + rtl8225_write_phy_ofdm(dev, 2, 0x62); + rtl8225_write_phy_ofdm(dev, 5, 0x00); + rtl8225_write_phy_ofdm(dev, 6, 0x40); + rtl8225_write_phy_ofdm(dev, 7, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x40); + + msleep(1); +} + +static const u16 rtl8225z2_rxgain[] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, + 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, + 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, + 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, + 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, + 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, + 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static void rtl8225z2_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); + + /* host_pci_init */ + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + msleep(200); /* FIXME: ehh?? */ + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008); + + /* TODO: check if we need really to change BRSR to do RF config */ + rtl818x_ioread16(priv, &priv->map->BRSR); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + rtl8225_write(dev, 0x0, 0x0B7); msleep(1); + rtl8225_write(dev, 0x1, 0xEE0); msleep(1); + rtl8225_write(dev, 0x2, 0x44D); msleep(1); + rtl8225_write(dev, 0x3, 0x441); msleep(1); + rtl8225_write(dev, 0x4, 0x8C3); msleep(1); + rtl8225_write(dev, 0x5, 0xC72); msleep(1); + rtl8225_write(dev, 0x6, 0x0E6); msleep(1); + rtl8225_write(dev, 0x7, 0x82A); msleep(1); + rtl8225_write(dev, 0x8, 0x03F); msleep(1); + rtl8225_write(dev, 0x9, 0x335); msleep(1); + rtl8225_write(dev, 0xa, 0x9D4); msleep(1); + rtl8225_write(dev, 0xb, 0x7BB); msleep(1); + rtl8225_write(dev, 0xc, 0x850); msleep(1); + rtl8225_write(dev, 0xd, 0xCDF); msleep(1); + rtl8225_write(dev, 0xe, 0x02B); msleep(1); + rtl8225_write(dev, 0xf, 0x114); msleep(100); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0C4D); + msleep(200); + rtl8225_write(dev, 0x02, 0x044D); + msleep(100); + /* TODO: readd calibration failure message when the calibration + check works */ + } + + rtl8225_write(dev, 0x0, 0x1B7); + rtl8225_write(dev, 0x3, 0x002); + rtl8225_write(dev, 0x5, 0x004); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x0B7); msleep(100); + rtl8225_write(dev, 0x2, 0xC4D); + + msleep(200); + rtl8225_write(dev, 0x2, 0x44D); + msleep(100); + + rtl8225_write(dev, 0x00, 0x2BF); + rtl8225_write(dev, 0xFF, 0xFFFF); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + msleep(1); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + msleep(1); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */ + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); + rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); + rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); + rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); + rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); + rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); + rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1); + rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); + rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); + rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1); + rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1); + rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1); + rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1); + rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1); + rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1); + rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1); + rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); + + rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1); + + rtl8225z2_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); +} + +static void rtl8225_rf_stop(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u8 reg; + + rtl8225_write(dev, 0x4, 0x1f); msleep(1); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); +} + +static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); + + if (priv->rf->init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, chan); + else + rtl8225z2_rf_set_tx_power(dev, chan); + + rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); + msleep(10); +} + +static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev, + struct ieee80211_bss_conf *info) +{ + struct rtl8180_priv *priv = dev->priv; + + if (info->use_short_slot) { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); + rtl818x_iowrite8(priv, &priv->map->EIFS, 81); + rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); + } else { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); + rtl818x_iowrite8(priv, &priv->map->EIFS, 81); + rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); + } +} + +static const struct rtl818x_rf_ops rtl8225_ops = { + .name = "rtl8225", + .init = rtl8225_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel, + .conf_erp = rtl8225_rf_conf_erp, +}; + +static const struct rtl818x_rf_ops rtl8225z2_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel, + .conf_erp = rtl8225_rf_conf_erp, +}; + +const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg8, reg9; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + msleep(100); + + rtl8225_write(dev, 0, 0x1B7); + + reg8 = rtl8225_read(dev, 8); + reg9 = rtl8225_read(dev, 9); + + rtl8225_write(dev, 0, 0x0B7); + + if (reg8 != 0x588 || reg9 != 0x700) + return &rtl8225_ops; + + return &rtl8225z2_ops; +} diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h new file mode 100644 index 000000000000..310013a2d726 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h @@ -0,0 +1,23 @@ +#ifndef RTL8180_RTL8225_H +#define RTL8180_RTL8225_H + +#define RTL8225_ANAPARAM_ON 0xa0000b59 +#define RTL8225_ANAPARAM2_ON 0x860dec11 +#define RTL8225_ANAPARAM_OFF 0xa00beb59 +#define RTL8225_ANAPARAM2_OFF 0x840dec11 + +const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *); + +static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, + u8 addr, u8 data) +{ + rtl8180_write_phy(dev, addr, data); +} + +static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, + u8 addr, u8 data) +{ + rtl8180_write_phy(dev, addr, data | 0x10000); +} + +#endif /* RTL8180_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.c b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c new file mode 100644 index 000000000000..44771a6286af --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c @@ -0,0 +1,228 @@ + +/* + * Radio tuning for Philips SA2400 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "sa2400.h" + +static const u32 sa2400_chan[] = { + 0x00096c, /* ch1 */ + 0x080970, + 0x100974, + 0x180978, + 0x000980, + 0x080984, + 0x100988, + 0x18098c, + 0x000994, + 0x080998, + 0x10099c, + 0x1809a0, + 0x0009a8, + 0x0009b4, /* ch 14 */ +}; + +static void write_sa2400(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + u32 phy_config; + + /* MAC will bang bits to the sa2400. sw 3-wire is NOT used */ + phy_config = 0xb0000000; + + phy_config |= ((u32)(addr & 0xf)) << 24; + phy_config |= data & 0xffffff; + + rtl818x_iowrite32(priv, + (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); + + msleep(3); +} + +static void sa2400_write_phy_antenna(struct ieee80211_hw *dev, short chan) +{ + struct rtl8180_priv *priv = dev->priv; + u8 ant = SA2400_ANTENNA; + + if (priv->rfparam & RF_PARAM_ANTBDEFAULT) + ant |= BB_ANTENNA_B; + + if (chan == 14) + ant |= BB_ANTATTEN_CHAN14; + + rtl8180_write_phy(dev, 0x10, ant); + +} + +static u8 sa2400_rf_rssi_map[] = { + 0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, + 0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50, + 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f, + 0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b, + 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, + 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, + 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, + 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, +}; + +static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq) +{ + if (sq == 0x80) + return 1; + + if (sq > 78) + return 32; + + /* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */ + return 65 * sa2400_rf_rssi_map[sq] / 100; +} + +static void sa2400_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; + u32 chan = sa2400_chan[channel - 1]; + + write_sa2400(dev, 7, txpw); + + sa2400_write_phy_antenna(dev, channel); + + write_sa2400(dev, 0, chan); + write_sa2400(dev, 1, 0xbb50); + write_sa2400(dev, 2, 0x80); + write_sa2400(dev, 3, 0); +} + +static void sa2400_rf_stop(struct ieee80211_hw *dev) +{ + write_sa2400(dev, 4, 0); +} + +static void sa2400_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u32 anaparam, txconf; + u8 firdac; + int analogphy = priv->rfparam & RF_PARAM_ANALOGPHY; + + anaparam = priv->anaparam; + anaparam &= ~(1 << ANAPARAM_TXDACOFF_SHIFT); + anaparam &= ~ANAPARAM_PWR1_MASK; + anaparam &= ~ANAPARAM_PWR0_MASK; + + if (analogphy) { + anaparam |= SA2400_ANA_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT; + firdac = 0; + } else { + anaparam |= (SA2400_DIG_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT); + anaparam |= (SA2400_ANAPARAM_PWR0_ON << ANAPARAM_PWR0_SHIFT); + firdac = 1 << SA2400_REG4_FIRDAC_SHIFT; + } + + rtl8180_set_anaparam(priv, anaparam); + + write_sa2400(dev, 0, sa2400_chan[0]); + write_sa2400(dev, 1, 0xbb50); + write_sa2400(dev, 2, 0x80); + write_sa2400(dev, 3, 0); + write_sa2400(dev, 4, 0x19340 | firdac); + write_sa2400(dev, 5, 0x1dfb | (SA2400_MAX_SENS - 54) << 15); + write_sa2400(dev, 4, 0x19348 | firdac); /* calibrate VCO */ + + if (!analogphy) + write_sa2400(dev, 4, 0x1938c); /*???*/ + + write_sa2400(dev, 4, 0x19340 | firdac); + + write_sa2400(dev, 0, sa2400_chan[0]); + write_sa2400(dev, 1, 0xbb50); + write_sa2400(dev, 2, 0x80); + write_sa2400(dev, 3, 0); + write_sa2400(dev, 4, 0x19344 | firdac); /* calibrate filter */ + + /* new from rtl8180 embedded driver (rtl8181 project) */ + write_sa2400(dev, 6, 0x13ff | (1 << 23)); /* MANRX */ + write_sa2400(dev, 8, 0); /* VCO */ + + if (analogphy) { + rtl8180_set_anaparam(priv, anaparam | + (1 << ANAPARAM_TXDACOFF_SHIFT)); + + txconf = rtl818x_ioread32(priv, &priv->map->TX_CONF); + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + txconf | RTL818X_TX_CONF_LOOPBACK_CONT); + + write_sa2400(dev, 4, 0x19341); /* calibrates DC */ + + /* a 5us sleep is required here, + * we rely on the 3ms delay introduced in write_sa2400 */ + write_sa2400(dev, 4, 0x19345); + + /* a 20us sleep is required here, + * we rely on the 3ms delay introduced in write_sa2400 */ + + rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf); + + rtl8180_set_anaparam(priv, anaparam); + } + /* end new code */ + + write_sa2400(dev, 4, 0x19341 | firdac); /* RTX MODE */ + + /* baseband configuration */ + rtl8180_write_phy(dev, 0, 0x98); + rtl8180_write_phy(dev, 3, 0x38); + rtl8180_write_phy(dev, 4, 0xe0); + rtl8180_write_phy(dev, 5, 0x90); + rtl8180_write_phy(dev, 6, 0x1a); + rtl8180_write_phy(dev, 7, 0x64); + + sa2400_write_phy_antenna(dev, 1); + + rtl8180_write_phy(dev, 0x11, 0x80); + + if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & + RTL818X_CONFIG2_ANTENNA_DIV) + rtl8180_write_phy(dev, 0x12, 0xc7); /* enable ant diversity */ + else + rtl8180_write_phy(dev, 0x12, 0x47); /* disable ant diversity */ + + rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); + + rtl8180_write_phy(dev, 0x19, 0x0); + rtl8180_write_phy(dev, 0x1a, 0xa0); +} + +const struct rtl818x_rf_ops sa2400_rf_ops = { + .name = "Philips", + .init = sa2400_rf_init, + .stop = sa2400_rf_stop, + .set_chan = sa2400_rf_set_channel, + .calc_rssi = sa2400_rf_calc_rssi, +}; diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.h b/drivers/net/wireless/rtl818x/rtl8180/sa2400.h new file mode 100644 index 000000000000..a4aaa0d413f1 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.h @@ -0,0 +1,36 @@ +#ifndef RTL8180_SA2400_H +#define RTL8180_SA2400_H + +/* + * Radio tuning for Philips SA2400 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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. + */ + +#define SA2400_ANTENNA 0x91 +#define SA2400_DIG_ANAPARAM_PWR1_ON 0x8 +#define SA2400_ANA_ANAPARAM_PWR1_ON 0x28 +#define SA2400_ANAPARAM_PWR0_ON 0x3 + +/* RX sensitivity in dbm */ +#define SA2400_MAX_SENS 85 + +#define SA2400_REG4_FIRDAC_SHIFT 7 + +extern const struct rtl818x_rf_ops sa2400_rf_ops; + +#endif /* RTL8180_SA2400_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c deleted file mode 100644 index 707c688da618..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ /dev/null @@ -1,1188 +0,0 @@ - -/* - * Linux device driver for RTL8180 / RTL8185 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8180 driver, which is: - * Copyright 2004-2005 Andrea Merello , et al. - * - * Thanks to Realtek for their support! - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_rtl8225.h" -#include "rtl8180_sa2400.h" -#include "rtl8180_max2820.h" -#include "rtl8180_grf5101.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_AUTHOR("Andrea Merello "); -MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); -MODULE_LICENSE("GPL"); - -static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { - /* rtl8185 */ - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, - { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) }, - { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) }, - - /* rtl8180 */ - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) }, - { PCI_DEVICE(0x1799, 0x6001) }, - { PCI_DEVICE(0x1799, 0x6020) }, - { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) }, - { } -}; - -MODULE_DEVICE_TABLE(pci, rtl8180_table); - -static const struct ieee80211_rate rtl818x_rates[] = { - { .bitrate = 10, .hw_value = 0, }, - { .bitrate = 20, .hw_value = 1, }, - { .bitrate = 55, .hw_value = 2, }, - { .bitrate = 110, .hw_value = 3, }, - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static const struct ieee80211_channel rtl818x_channels[] = { - { .center_freq = 2412 }, - { .center_freq = 2417 }, - { .center_freq = 2422 }, - { .center_freq = 2427 }, - { .center_freq = 2432 }, - { .center_freq = 2437 }, - { .center_freq = 2442 }, - { .center_freq = 2447 }, - { .center_freq = 2452 }, - { .center_freq = 2457 }, - { .center_freq = 2462 }, - { .center_freq = 2467 }, - { .center_freq = 2472 }, - { .center_freq = 2484 }, -}; - - -void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - int i = 10; - u32 buf; - - buf = (data << 8) | addr; - - rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80); - while (i--) { - rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf); - if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF)) - return; - } -} - -static void rtl8180_handle_rx(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - unsigned int count = 32; - u8 signal, agc, sq; - - while (count--) { - struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; - struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; - u32 flags = le32_to_cpu(entry->flags); - - if (flags & RTL818X_RX_DESC_FLAG_OWN) - return; - - if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | - RTL818X_RX_DESC_FLAG_FOF | - RTL818X_RX_DESC_FLAG_RX_ERR))) - goto done; - else { - u32 flags2 = le32_to_cpu(entry->flags2); - struct ieee80211_rx_status rx_status = {0}; - struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE); - - if (unlikely(!new_skb)) - goto done; - - pci_unmap_single(priv->pdev, - *((dma_addr_t *)skb->cb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - skb_put(skb, flags & 0xFFF); - - rx_status.antenna = (flags2 >> 15) & 1; - rx_status.rate_idx = (flags >> 20) & 0xF; - agc = (flags2 >> 17) & 0x7F; - if (priv->r8185) { - if (rx_status.rate_idx > 3) - signal = 90 - clamp_t(u8, agc, 25, 90); - else - signal = 95 - clamp_t(u8, agc, 30, 95); - } else { - sq = flags2 & 0xff; - signal = priv->rf->calc_rssi(agc, sq); - } - rx_status.signal = signal; - rx_status.freq = dev->conf.channel->center_freq; - rx_status.band = dev->conf.channel->band; - rx_status.mactime = le64_to_cpu(entry->tsft); - rx_status.flag |= RX_FLAG_TSFT; - if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) - rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(dev, skb); - - skb = new_skb; - priv->rx_buf[priv->rx_idx] = skb; - *((dma_addr_t *) skb->cb) = - pci_map_single(priv->pdev, skb_tail_pointer(skb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - } - - done: - entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); - entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | - MAX_RX_SIZE); - if (priv->rx_idx == 31) - entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); - priv->rx_idx = (priv->rx_idx + 1) % 32; - } -} - -static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; - - while (skb_queue_len(&ring->queue)) { - struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - u32 flags = le32_to_cpu(entry->flags); - - if (flags & RTL818X_TX_DESC_FLAG_OWN) - return; - - ring->idx = (ring->idx + 1) % ring->entries; - skb = __skb_dequeue(&ring->queue); - pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), - skb->len, PCI_DMA_TODEVICE); - - info = IEEE80211_SKB_CB(skb); - ieee80211_tx_info_clear_status(info); - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && - (flags & RTL818X_TX_DESC_FLAG_TX_OK)) - info->flags |= IEEE80211_TX_STAT_ACK; - - info->status.rates[0].count = (flags & 0xFF) + 1; - info->status.rates[1].idx = -1; - - ieee80211_tx_status_irqsafe(dev, skb); - if (ring->entries - skb_queue_len(&ring->queue) == 2) - ieee80211_wake_queue(dev, prio); - } -} - -static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) -{ - struct ieee80211_hw *dev = dev_id; - struct rtl8180_priv *priv = dev->priv; - u16 reg; - - spin_lock(&priv->lock); - reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS); - if (unlikely(reg == 0xFFFF)) { - spin_unlock(&priv->lock); - return IRQ_HANDLED; - } - - rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg); - - if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR)) - rtl8180_handle_tx(dev, 3); - - if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR)) - rtl8180_handle_tx(dev, 2); - - if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR)) - rtl8180_handle_tx(dev, 1); - - if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR)) - rtl8180_handle_tx(dev, 0); - - if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR)) - rtl8180_handle_rx(dev); - - spin_unlock(&priv->lock); - - return IRQ_HANDLED; -} - -static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_ring *ring; - struct rtl8180_tx_desc *entry; - unsigned long flags; - unsigned int idx, prio; - dma_addr_t mapping; - u32 tx_flags; - u8 rc_flags; - u16 plcp_len = 0; - __le16 rts_duration = 0; - - prio = skb_get_queue_mapping(skb); - ring = &priv->tx_ring[prio]; - - mapping = pci_map_single(priv->pdev, skb->data, - skb->len, PCI_DMA_TODEVICE); - - tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | - RTL818X_TX_DESC_FLAG_LS | - (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | - skb->len; - - if (priv->r8185) - tx_flags |= RTL818X_TX_DESC_FLAG_DMA | - RTL818X_TX_DESC_FLAG_NO_ENC; - - rc_flags = info->control.rates[0].flags; - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { - tx_flags |= RTL818X_TX_DESC_FLAG_RTS; - tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - tx_flags |= RTL818X_TX_DESC_FLAG_CTS; - tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - } - - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) - rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, - info); - - if (!priv->r8185) { - unsigned int remainder; - - plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), - (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); - remainder = (16 * (skb->len + 4)) % - ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); - if (remainder <= 6) - plcp_len |= 1 << 15; - } - - spin_lock_irqsave(&priv->lock, flags); - - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - priv->seqno += 0x10; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(priv->seqno); - } - - idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; - entry = &ring->desc[idx]; - - entry->rts_duration = rts_duration; - entry->plcp_len = cpu_to_le16(plcp_len); - entry->tx_buf = cpu_to_le32(mapping); - entry->frame_len = cpu_to_le32(skb->len); - entry->flags2 = info->control.rates[1].idx >= 0 ? - ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; - entry->retry_limit = info->control.rates[0].count; - entry->flags = cpu_to_le32(tx_flags); - __skb_queue_tail(&ring->queue, skb); - if (ring->entries - skb_queue_len(&ring->queue) < 2) - ieee80211_stop_queue(dev, prio); - - spin_unlock_irqrestore(&priv->lock, flags); - - rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); - - return 0; -} - -void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) -{ - u8 reg; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); -} - -static int rtl8180_init_hw(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg; - - rtl818x_iowrite8(priv, &priv->map->CMD, 0); - rtl818x_ioread8(priv, &priv->map->CMD); - msleep(10); - - /* reset */ - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - rtl818x_ioread8(priv, &priv->map->CMD); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= (1 << 1); - reg |= RTL818X_CMD_RESET; - rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET); - rtl818x_ioread8(priv, &priv->map->CMD); - msleep(200); - - /* check success of reset */ - if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) { - wiphy_err(dev->wiphy, "reset timeout!\n"); - return -ETIMEDOUT; - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); - rtl818x_ioread8(priv, &priv->map->CMD); - msleep(200); - - if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) { - /* For cardbus */ - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - reg |= 1 << 1; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - reg = rtl818x_ioread16(priv, &priv->map->FEMR); - reg |= (1 << 15) | (1 << 14) | (1 << 4); - rtl818x_iowrite16(priv, &priv->map->FEMR, reg); - } - - rtl818x_iowrite8(priv, &priv->map->MSR, 0); - - if (!priv->r8185) - rtl8180_set_anaparam(priv, priv->anaparam); - - rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); - rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); - rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); - rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); - rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); - - /* TODO: necessary? specs indicate not */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); - rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3)); - if (priv->r8185) { - reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); - rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4)); - } - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */ - - /* TODO: turn off hw wep on rtl8180 */ - - rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); - - if (priv->r8185) { - rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); - rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); - - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - - /* TODO: set ClkRun enable? necessary? */ - reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6)); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - } else { - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1); - rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); - - rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); - rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); - } - - priv->rf->init(dev); - if (priv->r8185) - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - return 0; -} - -static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_rx_desc *entry; - int i; - - priv->rx_ring = pci_alloc_consistent(priv->pdev, - sizeof(*priv->rx_ring) * 32, - &priv->rx_ring_dma); - - if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) { - wiphy_err(dev->wiphy, "Cannot allocate RX ring\n"); - return -ENOMEM; - } - - memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32); - priv->rx_idx = 0; - - for (i = 0; i < 32; i++) { - struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE); - dma_addr_t *mapping; - entry = &priv->rx_ring[i]; - if (!skb) - return 0; - - priv->rx_buf[i] = skb; - mapping = (dma_addr_t *)skb->cb; - *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - entry->rx_buf = cpu_to_le32(*mapping); - entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | - MAX_RX_SIZE); - } - entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); - return 0; -} - -static void rtl8180_free_rx_ring(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - for (i = 0; i < 32; i++) { - struct sk_buff *skb = priv->rx_buf[i]; - if (!skb) - continue; - - pci_unmap_single(priv->pdev, - *((dma_addr_t *)skb->cb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - kfree_skb(skb); - } - - pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, - priv->rx_ring, priv->rx_ring_dma); - priv->rx_ring = NULL; -} - -static int rtl8180_init_tx_ring(struct ieee80211_hw *dev, - unsigned int prio, unsigned int entries) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_desc *ring; - dma_addr_t dma; - int i; - - ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma); - if (!ring || (unsigned long)ring & 0xFF) { - wiphy_err(dev->wiphy, "Cannot allocate TX ring (prio = %d)\n", - prio); - return -ENOMEM; - } - - memset(ring, 0, sizeof(*ring)*entries); - priv->tx_ring[prio].desc = ring; - priv->tx_ring[prio].dma = dma; - priv->tx_ring[prio].idx = 0; - priv->tx_ring[prio].entries = entries; - skb_queue_head_init(&priv->tx_ring[prio].queue); - - for (i = 0; i < entries; i++) - ring[i].next_tx_desc = - cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring)); - - return 0; -} - -static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; - - while (skb_queue_len(&ring->queue)) { - struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; - struct sk_buff *skb = __skb_dequeue(&ring->queue); - - pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), - skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); - ring->idx = (ring->idx + 1) % ring->entries; - } - - pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries, - ring->desc, ring->dma); - ring->desc = NULL; -} - -static int rtl8180_start(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int ret, i; - u32 reg; - - ret = rtl8180_init_rx_ring(dev); - if (ret) - return ret; - - for (i = 0; i < 4; i++) - if ((ret = rtl8180_init_tx_ring(dev, i, 16))) - goto err_free_rings; - - ret = rtl8180_init_hw(dev); - if (ret) - goto err_free_rings; - - rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); - rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); - rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); - rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); - rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); - - ret = request_irq(priv->pdev->irq, rtl8180_interrupt, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (ret) { - wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); - goto err_free_rings; - } - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); - - rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); - rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); - - reg = RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_MGMT | - RTL818X_RX_CONF_DATA | - (7 << 8 /* MAX RX DMA */) | - RTL818X_RX_CONF_BROADCAST | - RTL818X_RX_CONF_NICMAC; - - if (priv->r8185) - reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2; - else { - reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) - ? RTL818X_RX_CONF_CSDM1 : 0; - reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2) - ? RTL818X_RX_CONF_CSDM2 : 0; - } - - priv->rx_conf = reg; - rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - - if (priv->r8185) { - reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; - rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; - reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT; - rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); - - /* disable early TX */ - rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f); - } - - reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); - reg |= (6 << 21 /* MAX TX DMA */) | - RTL818X_TX_CONF_NO_ICV; - - if (priv->r8185) - reg &= ~RTL818X_TX_CONF_PROBE_DTS; - else - reg &= ~RTL818X_TX_CONF_HW_SEQNUM; - - /* different meaning, same value on both rtl8185 and rtl8180 */ - reg &= ~RTL818X_TX_CONF_SAT_HWPLCP; - - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg |= RTL818X_CMD_RX_ENABLE; - reg |= RTL818X_CMD_TX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - return 0; - - err_free_rings: - rtl8180_free_rx_ring(dev); - for (i = 0; i < 4; i++) - if (priv->tx_ring[i].desc) - rtl8180_free_tx_ring(dev, i); - - return ret; -} - -static void rtl8180_stop(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u8 reg; - int i; - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= ~RTL818X_CMD_TX_ENABLE; - reg &= ~RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - priv->rf->stop(dev); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); - rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - free_irq(priv->pdev->irq, dev); - - rtl8180_free_rx_ring(dev); - for (i = 0; i < 4; i++) - rtl8180_free_tx_ring(dev, i); -} - -static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - - return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | - (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; -} - -static void rtl8180_beacon_work(struct work_struct *work) -{ - struct rtl8180_vif *vif_priv = - container_of(work, struct rtl8180_vif, beacon_work.work); - struct ieee80211_vif *vif = - container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); - struct ieee80211_hw *dev = vif_priv->dev; - struct ieee80211_mgmt *mgmt; - struct sk_buff *skb; - int err = 0; - - /* don't overflow the tx ring */ - if (ieee80211_queue_stopped(dev, 0)) - goto resched; - - /* grab a fresh beacon */ - skb = ieee80211_beacon_get(dev, vif); - if (!skb) - goto resched; - - /* - * update beacon timestamp w/ TSF value - * TODO: make hardware update beacon timestamp - */ - mgmt = (struct ieee80211_mgmt *)skb->data; - mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev)); - - /* TODO: use actual beacon queue */ - skb_set_queue_mapping(skb, 0); - - err = rtl8180_tx(dev, skb); - WARN_ON(err); - -resched: - /* - * schedule next beacon - * TODO: use hardware support for beacon timing - */ - schedule_delayed_work(&vif_priv->beacon_work, - usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); -} - -static int rtl8180_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_vif *vif_priv; - - /* - * We only support one active interface at a time. - */ - if (priv->vif) - return -EBUSY; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - break; - default: - return -EOPNOTSUPP; - } - - priv->vif = vif; - - /* Initialize driver private area */ - vif_priv = (struct rtl8180_vif *)&vif->drv_priv; - vif_priv->dev = dev; - INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8180_beacon_work); - vif_priv->enable_beacon = false; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], - le32_to_cpu(*(__le32 *)vif->addr)); - rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], - le16_to_cpu(*(__le16 *)(vif->addr + 4))); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - return 0; -} - -static void rtl8180_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8180_priv *priv = dev->priv; - priv->vif = NULL; -} - -static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) -{ - struct rtl8180_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - - priv->rf->set_chan(dev, conf); - - return 0; -} - -static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_vif *vif_priv; - int i; - u8 reg; - - vif_priv = (struct rtl8180_vif *)&vif->drv_priv; - - if (changed & BSS_CHANGED_BSSID) { - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], - info->bssid[i]); - - if (is_valid_ether_addr(info->bssid)) { - if (vif->type == NL80211_IFTYPE_ADHOC) - reg = RTL818X_MSR_ADHOC; - else - reg = RTL818X_MSR_INFRA; - } else - reg = RTL818X_MSR_NO_LINK; - rtl818x_iowrite8(priv, &priv->map->MSR, reg); - } - - if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) - priv->rf->conf_erp(dev, info); - - if (changed & BSS_CHANGED_BEACON_ENABLED) - vif_priv->enable_beacon = info->enable_beacon; - - if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { - cancel_delayed_work_sync(&vif_priv->beacon_work); - if (vif_priv->enable_beacon) - schedule_work(&vif_priv->beacon_work.work); - } -} - -static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, - struct netdev_hw_addr_list *mc_list) -{ - return netdev_hw_addr_list_count(mc_list); -} - -static void rtl8180_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct rtl8180_priv *priv = dev->priv; - - if (changed_flags & FIF_FCSFAIL) - priv->rx_conf ^= RTL818X_RX_CONF_FCS; - if (changed_flags & FIF_CONTROL) - priv->rx_conf ^= RTL818X_RX_CONF_CTRL; - if (changed_flags & FIF_OTHER_BSS) - priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || multicast > 0) - priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; - else - priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; - - *total_flags = 0; - - if (priv->rx_conf & RTL818X_RX_CONF_FCS) - *total_flags |= FIF_FCSFAIL; - if (priv->rx_conf & RTL818X_RX_CONF_CTRL) - *total_flags |= FIF_CONTROL; - if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) - *total_flags |= FIF_OTHER_BSS; - if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) - *total_flags |= FIF_ALLMULTI; - - rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf); -} - -static const struct ieee80211_ops rtl8180_ops = { - .tx = rtl8180_tx, - .start = rtl8180_start, - .stop = rtl8180_stop, - .add_interface = rtl8180_add_interface, - .remove_interface = rtl8180_remove_interface, - .config = rtl8180_config, - .bss_info_changed = rtl8180_bss_info_changed, - .prepare_multicast = rtl8180_prepare_multicast, - .configure_filter = rtl8180_configure_filter, - .get_tsf = rtl8180_get_tsf, -}; - -static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8180_priv *priv = dev->priv; - u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - - eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; - eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; - eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; - eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; -} - -static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8180_priv *priv = dev->priv; - u8 reg = 2 << 6; - - if (eeprom->reg_data_in) - reg |= RTL818X_EEPROM_CMD_WRITE; - if (eeprom->reg_data_out) - reg |= RTL818X_EEPROM_CMD_READ; - if (eeprom->reg_data_clock) - reg |= RTL818X_EEPROM_CMD_CK; - if (eeprom->reg_chip_select) - reg |= RTL818X_EEPROM_CMD_CS; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); -} - -static int __devinit rtl8180_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct ieee80211_hw *dev; - struct rtl8180_priv *priv; - unsigned long mem_addr, mem_len; - unsigned int io_addr, io_len; - int err, i; - struct eeprom_93cx6 eeprom; - const char *chip_name, *rf_name = NULL; - u32 reg; - u16 eeprom_val; - u8 mac_addr[ETH_ALEN]; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n", - pci_name(pdev)); - return err; - } - - err = pci_request_regions(pdev, KBUILD_MODNAME); - if (err) { - printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n", - pci_name(pdev)); - return err; - } - - io_addr = pci_resource_start(pdev, 0); - io_len = pci_resource_len(pdev, 0); - mem_addr = pci_resource_start(pdev, 1); - mem_len = pci_resource_len(pdev, 1); - - if (mem_len < sizeof(struct rtl818x_csr) || - io_len < sizeof(struct rtl818x_csr)) { - printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n", - pci_name(pdev)); - err = -ENOMEM; - goto err_free_reg; - } - - if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) || - (err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))) { - printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n", - pci_name(pdev)); - goto err_free_reg; - } - - pci_set_master(pdev); - - dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops); - if (!dev) { - printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n", - pci_name(pdev)); - err = -ENOMEM; - goto err_free_reg; - } - - priv = dev->priv; - priv->pdev = pdev; - - dev->max_rates = 2; - SET_IEEE80211_DEV(dev, &pdev->dev); - pci_set_drvdata(pdev, dev); - - priv->map = pci_iomap(pdev, 1, mem_len); - if (!priv->map) - priv->map = pci_iomap(pdev, 0, io_len); - - if (!priv->map) { - printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n", - pci_name(pdev)); - goto err_free_dev; - } - - BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); - - memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); - memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); - - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = 4; - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_UNSPEC; - dev->vif_data_size = sizeof(struct rtl8180_vif); - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); - dev->queues = 1; - dev->max_signal = 65; - - reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); - reg &= RTL818X_TX_CONF_HWVER_MASK; - switch (reg) { - case RTL818X_TX_CONF_R8180_ABCD: - chip_name = "RTL8180"; - break; - case RTL818X_TX_CONF_R8180_F: - chip_name = "RTL8180vF"; - break; - case RTL818X_TX_CONF_R8185_ABC: - chip_name = "RTL8185"; - break; - case RTL818X_TX_CONF_R8185_D: - chip_name = "RTL8185vD"; - break; - default: - printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", - pci_name(pdev), reg >> 25); - goto err_iounmap; - } - - priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; - if (priv->r8185) { - priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); - pci_try_set_mwi(pdev); - } - - eeprom.data = dev; - eeprom.register_read = rtl8180_eeprom_register_read; - eeprom.register_write = rtl8180_eeprom_register_write; - if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) - eeprom.width = PCI_EEPROM_WIDTH_93C66; - else - eeprom.width = PCI_EEPROM_WIDTH_93C46; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); - eeprom_val &= 0xFF; - switch (eeprom_val) { - case 1: rf_name = "Intersil"; - break; - case 2: rf_name = "RFMD"; - break; - case 3: priv->rf = &sa2400_rf_ops; - break; - case 4: priv->rf = &max2820_rf_ops; - break; - case 5: priv->rf = &grf5101_rf_ops; - break; - case 9: priv->rf = rtl8180_detect_rf(dev); - break; - case 10: - rf_name = "RTL8255"; - break; - default: - printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", - pci_name(pdev), eeprom_val); - goto err_iounmap; - } - - if (!priv->rf) { - printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n", - pci_name(pdev), rf_name); - goto err_iounmap; - } - - eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); - priv->csthreshold = eeprom_val >> 8; - if (!priv->r8185) { - __le32 anaparam; - eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); - priv->anaparam = le32_to_cpu(anaparam); - eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); - } - - eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3); - if (!is_valid_ether_addr(mac_addr)) { - printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" - " randomly generated MAC addr\n", pci_name(pdev)); - random_ether_addr(mac_addr); - } - SET_IEEE80211_PERM_ADDR(dev, mac_addr); - - /* CCK TX power */ - for (i = 0; i < 14; i += 2) { - u16 txpwr; - eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); - priv->channels[i].hw_value = txpwr & 0xFF; - priv->channels[i + 1].hw_value = txpwr >> 8; - } - - /* OFDM TX power */ - if (priv->r8185) { - for (i = 0; i < 14; i += 2) { - u16 txpwr; - eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); - priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; - priv->channels[i + 1].hw_value |= txpwr & 0xFF00; - } - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - spin_lock_init(&priv->lock); - - err = ieee80211_register_hw(dev); - if (err) { - printk(KERN_ERR "%s (rtl8180): Cannot register device\n", - pci_name(pdev)); - goto err_iounmap; - } - - wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n", - mac_addr, chip_name, priv->rf->name); - - return 0; - - err_iounmap: - iounmap(priv->map); - - 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; -} - -static void __devexit rtl8180_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct rtl8180_priv *priv; - - if (!dev) - return; - - ieee80211_unregister_hw(dev); - - priv = dev->priv; - - pci_iounmap(pdev, priv->map); - pci_release_regions(pdev); - pci_disable_device(pdev); - ieee80211_free_hw(dev); -} - -#ifdef CONFIG_PM -static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state) -{ - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int rtl8180_resume(struct pci_dev *pdev) -{ - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - return 0; -} - -#endif /* CONFIG_PM */ - -static struct pci_driver rtl8180_driver = { - .name = KBUILD_MODNAME, - .id_table = rtl8180_table, - .probe = rtl8180_probe, - .remove = __devexit_p(rtl8180_remove), -#ifdef CONFIG_PM - .suspend = rtl8180_suspend, - .resume = rtl8180_resume, -#endif /* CONFIG_PM */ -}; - -static int __init rtl8180_init(void) -{ - return pci_register_driver(&rtl8180_driver); -} - -static void __exit rtl8180_exit(void) -{ - pci_unregister_driver(&rtl8180_driver); -} - -module_init(rtl8180_init); -module_exit(rtl8180_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8180_grf5101.c b/drivers/net/wireless/rtl818x/rtl8180_grf5101.c deleted file mode 100644 index 5cab9dfa8c07..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_grf5101.c +++ /dev/null @@ -1,190 +0,0 @@ - -/* - * Radio tuning for GCT GRF5101 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_grf5101.h" - -static const int grf5101_encode[] = { - 0x0, 0x8, 0x4, 0xC, - 0x2, 0xA, 0x6, 0xE, - 0x1, 0x9, 0x5, 0xD, - 0x3, 0xB, 0x7, 0xF -}; - -static void write_grf5101(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - u32 phy_config; - - phy_config = grf5101_encode[(data >> 8) & 0xF]; - phy_config |= grf5101_encode[(data >> 4) & 0xF] << 4; - phy_config |= grf5101_encode[data & 0xF] << 8; - phy_config |= grf5101_encode[(addr >> 1) & 0xF] << 12; - phy_config |= (addr & 1) << 16; - phy_config |= grf5101_encode[(data & 0xf000) >> 12] << 24; - - /* MAC will bang bits to the chip */ - phy_config |= 0x90000000; - - rtl818x_iowrite32(priv, - (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); - - msleep(3); -} - -static void grf5101_write_phy_antenna(struct ieee80211_hw *dev, short chan) -{ - struct rtl8180_priv *priv = dev->priv; - u8 ant = GRF5101_ANTENNA; - - if (priv->rfparam & RF_PARAM_ANTBDEFAULT) - ant |= BB_ANTENNA_B; - - if (chan == 14) - ant |= BB_ANTATTEN_CHAN14; - - rtl8180_write_phy(dev, 0x10, ant); -} - -static u8 grf5101_rf_calc_rssi(u8 agc, u8 sq) -{ - if (agc > 60) - return 65; - - /* TODO(?): just return agc (or agc + 5) to avoid mult / div */ - return 65 * agc / 60; -} - -static void grf5101_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; - u32 chan = channel - 1; - - /* set TX power */ - write_grf5101(dev, 0x15, 0x0); - write_grf5101(dev, 0x06, txpw); - write_grf5101(dev, 0x15, 0x10); - write_grf5101(dev, 0x15, 0x0); - - /* set frequency */ - write_grf5101(dev, 0x07, 0x0); - write_grf5101(dev, 0x0B, chan); - write_grf5101(dev, 0x07, 0x1000); - - grf5101_write_phy_antenna(dev, channel); -} - -static void grf5101_rf_stop(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u32 anaparam; - - anaparam = priv->anaparam; - anaparam &= 0x000fffff; - anaparam |= 0x3f900000; - rtl8180_set_anaparam(priv, anaparam); - - write_grf5101(dev, 0x07, 0x0); - write_grf5101(dev, 0x1f, 0x45); - write_grf5101(dev, 0x1f, 0x5); - write_grf5101(dev, 0x00, 0x8e4); -} - -static void grf5101_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - - rtl8180_set_anaparam(priv, priv->anaparam); - - write_grf5101(dev, 0x1f, 0x0); - write_grf5101(dev, 0x1f, 0x0); - write_grf5101(dev, 0x1f, 0x40); - write_grf5101(dev, 0x1f, 0x60); - write_grf5101(dev, 0x1f, 0x61); - write_grf5101(dev, 0x1f, 0x61); - write_grf5101(dev, 0x00, 0xae4); - write_grf5101(dev, 0x1f, 0x1); - write_grf5101(dev, 0x1f, 0x41); - write_grf5101(dev, 0x1f, 0x61); - - write_grf5101(dev, 0x01, 0x1a23); - write_grf5101(dev, 0x02, 0x4971); - write_grf5101(dev, 0x03, 0x41de); - write_grf5101(dev, 0x04, 0x2d80); - write_grf5101(dev, 0x05, 0x68ff); /* 0x61ff original value */ - write_grf5101(dev, 0x06, 0x0); - write_grf5101(dev, 0x07, 0x0); - write_grf5101(dev, 0x08, 0x7533); - write_grf5101(dev, 0x09, 0xc401); - write_grf5101(dev, 0x0a, 0x0); - write_grf5101(dev, 0x0c, 0x1c7); - write_grf5101(dev, 0x0d, 0x29d3); - write_grf5101(dev, 0x0e, 0x2e8); - write_grf5101(dev, 0x10, 0x192); - write_grf5101(dev, 0x11, 0x248); - write_grf5101(dev, 0x12, 0x0); - write_grf5101(dev, 0x13, 0x20c4); - write_grf5101(dev, 0x14, 0xf4fc); - write_grf5101(dev, 0x15, 0x0); - write_grf5101(dev, 0x16, 0x1500); - - write_grf5101(dev, 0x07, 0x1000); - - /* baseband configuration */ - rtl8180_write_phy(dev, 0, 0xa8); - rtl8180_write_phy(dev, 3, 0x0); - rtl8180_write_phy(dev, 4, 0xc0); - rtl8180_write_phy(dev, 5, 0x90); - rtl8180_write_phy(dev, 6, 0x1e); - rtl8180_write_phy(dev, 7, 0x64); - - grf5101_write_phy_antenna(dev, 1); - - rtl8180_write_phy(dev, 0x11, 0x88); - - if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & - RTL818X_CONFIG2_ANTENNA_DIV) - rtl8180_write_phy(dev, 0x12, 0xc0); /* enable ant diversity */ - else - rtl8180_write_phy(dev, 0x12, 0x40); /* disable ant diversity */ - - rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); - - rtl8180_write_phy(dev, 0x19, 0x0); - rtl8180_write_phy(dev, 0x1a, 0xa0); - rtl8180_write_phy(dev, 0x1b, 0x44); -} - -const struct rtl818x_rf_ops grf5101_rf_ops = { - .name = "GCT", - .init = grf5101_rf_init, - .stop = grf5101_rf_stop, - .set_chan = grf5101_rf_set_channel, - .calc_rssi = grf5101_rf_calc_rssi, -}; diff --git a/drivers/net/wireless/rtl818x/rtl8180_grf5101.h b/drivers/net/wireless/rtl818x/rtl8180_grf5101.h deleted file mode 100644 index 76647111bcff..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_grf5101.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef RTL8180_GRF5101_H -#define RTL8180_GRF5101_H - -/* - * Radio tuning for GCT GRF5101 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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. - */ - -#define GRF5101_ANTENNA 0xA3 - -extern const struct rtl818x_rf_ops grf5101_rf_ops; - -#endif /* RTL8180_GRF5101_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_max2820.c b/drivers/net/wireless/rtl818x/rtl8180_max2820.c deleted file mode 100644 index 16c4655181c0..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_max2820.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Radio tuning for Maxim max2820 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_max2820.h" - -static const u32 max2820_chan[] = { - 12, /* CH 1 */ - 17, - 22, - 27, - 32, - 37, - 42, - 47, - 52, - 57, - 62, - 67, - 72, - 84, /* CH 14 */ -}; - -static void write_max2820(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - u32 phy_config; - - phy_config = 0x90 + (data & 0xf); - phy_config <<= 16; - phy_config += addr; - phy_config <<= 8; - phy_config += (data >> 4) & 0xff; - - rtl818x_iowrite32(priv, - (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); - - msleep(1); -} - -static void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan) -{ - struct rtl8180_priv *priv = dev->priv; - u8 ant; - - ant = MAXIM_ANTENNA; - if (priv->rfparam & RF_PARAM_ANTBDEFAULT) - ant |= BB_ANTENNA_B; - if (chan == 14) - ant |= BB_ANTATTEN_CHAN14; - - rtl8180_write_phy(dev, 0x10, ant); -} - -static u8 max2820_rf_calc_rssi(u8 agc, u8 sq) -{ - bool odd; - - odd = !!(agc & 1); - - agc >>= 1; - if (odd) - agc += 76; - else - agc += 66; - - /* TODO: change addends above to avoid mult / div below */ - return 65 * agc / 100; -} - -static void max2820_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int channel = conf ? - ieee80211_frequency_to_channel(conf->channel->center_freq) : 1; - unsigned int chan_idx = channel - 1; - u32 txpw = priv->channels[chan_idx].hw_value & 0xFF; - u32 chan = max2820_chan[chan_idx]; - - /* While philips SA2400 drive the PA bias from - * sa2400, for MAXIM we do this directly from BB */ - rtl8180_write_phy(dev, 3, txpw); - - max2820_write_phy_antenna(dev, channel); - write_max2820(dev, 3, chan); -} - -static void max2820_rf_stop(struct ieee80211_hw *dev) -{ - rtl8180_write_phy(dev, 3, 0x8); - write_max2820(dev, 1, 0); -} - - -static void max2820_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - - /* MAXIM from netbsd driver */ - write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */ - write_max2820(dev, 1, 0x01e); /* enable register */ - write_max2820(dev, 2, 0x001); /* synt register */ - - max2820_rf_set_channel(dev, NULL); - - write_max2820(dev, 4, 0x313); /* rx register */ - - /* PA is driven directly by the BB, we keep the MAXIM bias - * at the highest value in case that setting it to lower - * values may introduce some further attenuation somewhere.. - */ - write_max2820(dev, 5, 0x00f); - - /* baseband configuration */ - rtl8180_write_phy(dev, 0, 0x88); /* sys1 */ - rtl8180_write_phy(dev, 3, 0x08); /* txagc */ - rtl8180_write_phy(dev, 4, 0xf8); /* lnadet */ - rtl8180_write_phy(dev, 5, 0x90); /* ifagcinit */ - rtl8180_write_phy(dev, 6, 0x1a); /* ifagclimit */ - rtl8180_write_phy(dev, 7, 0x64); /* ifagcdet */ - - max2820_write_phy_antenna(dev, 1); - - rtl8180_write_phy(dev, 0x11, 0x88); /* trl */ - - if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & - RTL818X_CONFIG2_ANTENNA_DIV) - rtl8180_write_phy(dev, 0x12, 0xc7); - else - rtl8180_write_phy(dev, 0x12, 0x47); - - rtl8180_write_phy(dev, 0x13, 0x9b); - - rtl8180_write_phy(dev, 0x19, 0x0); /* CHESTLIM */ - rtl8180_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM */ - - max2820_rf_set_channel(dev, NULL); -} - -const struct rtl818x_rf_ops max2820_rf_ops = { - .name = "Maxim", - .init = max2820_rf_init, - .stop = max2820_rf_stop, - .set_chan = max2820_rf_set_channel, - .calc_rssi = max2820_rf_calc_rssi, -}; diff --git a/drivers/net/wireless/rtl818x/rtl8180_max2820.h b/drivers/net/wireless/rtl818x/rtl8180_max2820.h deleted file mode 100644 index 61cf6d1e7d57..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_max2820.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef RTL8180_MAX2820_H -#define RTL8180_MAX2820_H - -/* - * Radio tuning for Maxim max2820 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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. - */ - -#define MAXIM_ANTENNA 0xb3 - -extern const struct rtl818x_rf_ops max2820_rf_ops; - -#endif /* RTL8180_MAX2820_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c deleted file mode 100644 index 69e4d4745dae..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c +++ /dev/null @@ -1,791 +0,0 @@ - -/* - * Radio tuning for RTL8225 on RTL8180 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8180 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * Thanks to Realtek for their support! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_rtl8225.h" - -static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg80, reg84, reg82; - u32 bangdata; - int i; - - bangdata = (data << 4) | (addr & 0xf); - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); - - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - for (i = 15; i >= 0; i--) { - u16 reg = reg80; - - if (bangdata & (1 << i)) - reg |= 1; - - if (i & 1) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - - if (!(i & 1)) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); -} - -static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg80, reg82, reg84, out; - int i; - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400; - - reg80 &= ~0xF; - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(4); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(5); - - for (i = 4; i >= 0; i--) { - u16 reg = reg80 | ((addr >> i) & 1); - - if (!(i & 1)) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(1); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - if (i & 1) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(1); - } - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - out = 0; - for (i = 11; i >= 0; i--) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(1); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) - out |= 1 << i; - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); - - return out; -} - -static const u16 rtl8225bcd_rxgain[] = { - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, - 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, - 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, - 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, - 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, - 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, - 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, - 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, - 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, - 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, - 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb -}; - -static const u8 rtl8225_agc[] = { - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, - 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, - 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, - 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, - 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, - 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, - 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, - 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, - 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, - 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 -}; - -static const u8 rtl8225_gain[] = { - 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ - 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ - 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ - 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ - 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ - 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ - 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ -}; - -static const u8 rtl8225_threshold[] = { - 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd -}; - -static const u8 rtl8225_tx_gain_cck_ofdm[] = { - 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e -}; - -static const u8 rtl8225_tx_power_cck[] = { - 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, - 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, - 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, - 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, - 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, - 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 -}; - -static const u8 rtl8225_tx_power_cck_ch14[] = { - 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225_tx_power_ofdm[] = { - 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 -}; - -static const u32 rtl8225_chan[] = { - 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, - 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 -}; - -static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8180_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - u32 reg; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xFF; - ofdm_power = priv->channels[channel - 1].hw_value >> 8; - - cck_power = min(cck_power, (u8)35); - ofdm_power = min(ofdm_power, (u8)35); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); - - if (channel == 14) - tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; - else - tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - msleep(1); /* FIXME: optional? */ - - /* anaparam2 on */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1); - - tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; - - rtl8225_write_phy_ofdm(dev, 5, *tmp); - rtl8225_write_phy_ofdm(dev, 7, *tmp); - - msleep(1); -} - -static void rtl8225_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); - - /* host_pci_init */ - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - msleep(200); /* FIXME: ehh?? */ - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); - - /* TODO: check if we need really to change BRSR to do RF config */ - rtl818x_ioread16(priv, &priv->map->BRSR); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); - rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl8225_write(dev, 0x0, 0x067); - rtl8225_write(dev, 0x1, 0xFE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x8BE); - rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */ - rtl8225_write(dev, 0x6, 0xAE6); - rtl8225_write(dev, 0x7, rtl8225_chan[0]); - rtl8225_write(dev, 0x8, 0x01F); - rtl8225_write(dev, 0x9, 0x334); - rtl8225_write(dev, 0xA, 0xFD4); - rtl8225_write(dev, 0xB, 0x391); - rtl8225_write(dev, 0xC, 0x050); - rtl8225_write(dev, 0xD, 0x6DB); - rtl8225_write(dev, 0xE, 0x029); - rtl8225_write(dev, 0xF, 0x914); msleep(1); - - rtl8225_write(dev, 0x2, 0xC4D); msleep(100); - - rtl8225_write(dev, 0x0, 0x127); - - for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); - } - - rtl8225_write(dev, 0x0, 0x027); - rtl8225_write(dev, 0x0, 0x22F); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - msleep(1); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - msleep(1); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); - rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); - rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); - rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); - rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); - rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); - rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1); - rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); - rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); - rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1); - rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1); - rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1); - rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1); - rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1); - rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1); - rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1); - rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); - - rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1); - - rtl8225_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - rtl8225_write(dev, 0x0c, 0x50); - /* set OFDM initial gain */ - rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]); - rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]); - /* set CCK threshold */ - rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]); -} - -static const u8 rtl8225z2_tx_power_cck_ch14[] = { - 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225z2_tx_power_cck_B[] = { - 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04 -}; - -static const u8 rtl8225z2_tx_power_cck_A[] = { - 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04 -}; - -static const u8 rtl8225z2_tx_power_cck[] = { - 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 -}; - -static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8180_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xFF; - ofdm_power = priv->channels[channel - 1].hw_value >> 8; - - if (channel == 14) - tmp = rtl8225z2_tx_power_cck_ch14; - else if (cck_power == 12) - tmp = rtl8225z2_tx_power_cck_B; - else if (cck_power == 13) - tmp = rtl8225z2_tx_power_cck_A; - else - tmp = rtl8225z2_tx_power_cck; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - cck_power = min(cck_power, (u8)35); - if (cck_power == 13 || cck_power == 14) - cck_power = 12; - if (cck_power >= 15) - cck_power -= 2; - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power); - rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK); - msleep(1); - - ofdm_power = min(ofdm_power, (u8)35); - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power); - - rtl8225_write_phy_ofdm(dev, 2, 0x62); - rtl8225_write_phy_ofdm(dev, 5, 0x00); - rtl8225_write_phy_ofdm(dev, 6, 0x40); - rtl8225_write_phy_ofdm(dev, 7, 0x00); - rtl8225_write_phy_ofdm(dev, 8, 0x40); - - msleep(1); -} - -static const u16 rtl8225z2_rxgain[] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, - 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, - 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, - 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, - 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, - 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, - 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, - 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, - 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, - 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, - 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, - 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb -}; - -static void rtl8225z2_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); - - /* host_pci_init */ - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - msleep(200); /* FIXME: ehh?? */ - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008); - - /* TODO: check if we need really to change BRSR to do RF config */ - rtl818x_ioread16(priv, &priv->map->BRSR); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); - rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - rtl8225_write(dev, 0x0, 0x0B7); msleep(1); - rtl8225_write(dev, 0x1, 0xEE0); msleep(1); - rtl8225_write(dev, 0x2, 0x44D); msleep(1); - rtl8225_write(dev, 0x3, 0x441); msleep(1); - rtl8225_write(dev, 0x4, 0x8C3); msleep(1); - rtl8225_write(dev, 0x5, 0xC72); msleep(1); - rtl8225_write(dev, 0x6, 0x0E6); msleep(1); - rtl8225_write(dev, 0x7, 0x82A); msleep(1); - rtl8225_write(dev, 0x8, 0x03F); msleep(1); - rtl8225_write(dev, 0x9, 0x335); msleep(1); - rtl8225_write(dev, 0xa, 0x9D4); msleep(1); - rtl8225_write(dev, 0xb, 0x7BB); msleep(1); - rtl8225_write(dev, 0xc, 0x850); msleep(1); - rtl8225_write(dev, 0xd, 0xCDF); msleep(1); - rtl8225_write(dev, 0xe, 0x02B); msleep(1); - rtl8225_write(dev, 0xf, 0x114); msleep(100); - - if (!(rtl8225_read(dev, 6) & (1 << 7))) { - rtl8225_write(dev, 0x02, 0x0C4D); - msleep(200); - rtl8225_write(dev, 0x02, 0x044D); - msleep(100); - /* TODO: readd calibration failure message when the calibration - check works */ - } - - rtl8225_write(dev, 0x0, 0x1B7); - rtl8225_write(dev, 0x3, 0x002); - rtl8225_write(dev, 0x5, 0x004); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); - } - - rtl8225_write(dev, 0x0, 0x0B7); msleep(100); - rtl8225_write(dev, 0x2, 0xC4D); - - msleep(200); - rtl8225_write(dev, 0x2, 0x44D); - msleep(100); - - rtl8225_write(dev, 0x00, 0x2BF); - rtl8225_write(dev, 0xFF, 0xFFFF); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - msleep(1); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - msleep(1); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */ - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); - rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); - rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); - rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); - rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); - rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); - rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1); - rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); - rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); - rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1); - rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1); - rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1); - rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1); - rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1); - rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1); - rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1); - rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); - - rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1); - - rtl8225z2_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); -} - -static void rtl8225_rf_stop(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u8 reg; - - rtl8225_write(dev, 0x4, 0x1f); msleep(1); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); -} - -static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); - - if (priv->rf->init == rtl8225_rf_init) - rtl8225_rf_set_tx_power(dev, chan); - else - rtl8225z2_rf_set_tx_power(dev, chan); - - rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); - msleep(10); -} - -static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev, - struct ieee80211_bss_conf *info) -{ - struct rtl8180_priv *priv = dev->priv; - - if (info->use_short_slot) { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); - rtl818x_iowrite8(priv, &priv->map->EIFS, 81); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); - } else { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); - rtl818x_iowrite8(priv, &priv->map->EIFS, 81); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); - } -} - -static const struct rtl818x_rf_ops rtl8225_ops = { - .name = "rtl8225", - .init = rtl8225_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel, - .conf_erp = rtl8225_rf_conf_erp, -}; - -static const struct rtl818x_rf_ops rtl8225z2_ops = { - .name = "rtl8225z2", - .init = rtl8225z2_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel, - .conf_erp = rtl8225_rf_conf_erp, -}; - -const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg8, reg9; - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - msleep(100); - - rtl8225_write(dev, 0, 0x1B7); - - reg8 = rtl8225_read(dev, 8); - reg9 = rtl8225_read(dev, 9); - - rtl8225_write(dev, 0, 0x0B7); - - if (reg8 != 0x588 || reg9 != 0x700) - return &rtl8225_ops; - - return &rtl8225z2_ops; -} diff --git a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h deleted file mode 100644 index 310013a2d726..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef RTL8180_RTL8225_H -#define RTL8180_RTL8225_H - -#define RTL8225_ANAPARAM_ON 0xa0000b59 -#define RTL8225_ANAPARAM2_ON 0x860dec11 -#define RTL8225_ANAPARAM_OFF 0xa00beb59 -#define RTL8225_ANAPARAM2_OFF 0x840dec11 - -const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *); - -static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, - u8 addr, u8 data) -{ - rtl8180_write_phy(dev, addr, data); -} - -static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, - u8 addr, u8 data) -{ - rtl8180_write_phy(dev, addr, data | 0x10000); -} - -#endif /* RTL8180_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_sa2400.c b/drivers/net/wireless/rtl818x/rtl8180_sa2400.c deleted file mode 100644 index d064fcc5ec08..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_sa2400.c +++ /dev/null @@ -1,228 +0,0 @@ - -/* - * Radio tuning for Philips SA2400 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_sa2400.h" - -static const u32 sa2400_chan[] = { - 0x00096c, /* ch1 */ - 0x080970, - 0x100974, - 0x180978, - 0x000980, - 0x080984, - 0x100988, - 0x18098c, - 0x000994, - 0x080998, - 0x10099c, - 0x1809a0, - 0x0009a8, - 0x0009b4, /* ch 14 */ -}; - -static void write_sa2400(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - u32 phy_config; - - /* MAC will bang bits to the sa2400. sw 3-wire is NOT used */ - phy_config = 0xb0000000; - - phy_config |= ((u32)(addr & 0xf)) << 24; - phy_config |= data & 0xffffff; - - rtl818x_iowrite32(priv, - (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); - - msleep(3); -} - -static void sa2400_write_phy_antenna(struct ieee80211_hw *dev, short chan) -{ - struct rtl8180_priv *priv = dev->priv; - u8 ant = SA2400_ANTENNA; - - if (priv->rfparam & RF_PARAM_ANTBDEFAULT) - ant |= BB_ANTENNA_B; - - if (chan == 14) - ant |= BB_ANTATTEN_CHAN14; - - rtl8180_write_phy(dev, 0x10, ant); - -} - -static u8 sa2400_rf_rssi_map[] = { - 0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, - 0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50, - 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f, - 0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b, - 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, - 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, - 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, - 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, - 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, - 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, -}; - -static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq) -{ - if (sq == 0x80) - return 1; - - if (sq > 78) - return 32; - - /* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */ - return 65 * sa2400_rf_rssi_map[sq] / 100; -} - -static void sa2400_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; - u32 chan = sa2400_chan[channel - 1]; - - write_sa2400(dev, 7, txpw); - - sa2400_write_phy_antenna(dev, channel); - - write_sa2400(dev, 0, chan); - write_sa2400(dev, 1, 0xbb50); - write_sa2400(dev, 2, 0x80); - write_sa2400(dev, 3, 0); -} - -static void sa2400_rf_stop(struct ieee80211_hw *dev) -{ - write_sa2400(dev, 4, 0); -} - -static void sa2400_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u32 anaparam, txconf; - u8 firdac; - int analogphy = priv->rfparam & RF_PARAM_ANALOGPHY; - - anaparam = priv->anaparam; - anaparam &= ~(1 << ANAPARAM_TXDACOFF_SHIFT); - anaparam &= ~ANAPARAM_PWR1_MASK; - anaparam &= ~ANAPARAM_PWR0_MASK; - - if (analogphy) { - anaparam |= SA2400_ANA_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT; - firdac = 0; - } else { - anaparam |= (SA2400_DIG_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT); - anaparam |= (SA2400_ANAPARAM_PWR0_ON << ANAPARAM_PWR0_SHIFT); - firdac = 1 << SA2400_REG4_FIRDAC_SHIFT; - } - - rtl8180_set_anaparam(priv, anaparam); - - write_sa2400(dev, 0, sa2400_chan[0]); - write_sa2400(dev, 1, 0xbb50); - write_sa2400(dev, 2, 0x80); - write_sa2400(dev, 3, 0); - write_sa2400(dev, 4, 0x19340 | firdac); - write_sa2400(dev, 5, 0x1dfb | (SA2400_MAX_SENS - 54) << 15); - write_sa2400(dev, 4, 0x19348 | firdac); /* calibrate VCO */ - - if (!analogphy) - write_sa2400(dev, 4, 0x1938c); /*???*/ - - write_sa2400(dev, 4, 0x19340 | firdac); - - write_sa2400(dev, 0, sa2400_chan[0]); - write_sa2400(dev, 1, 0xbb50); - write_sa2400(dev, 2, 0x80); - write_sa2400(dev, 3, 0); - write_sa2400(dev, 4, 0x19344 | firdac); /* calibrate filter */ - - /* new from rtl8180 embedded driver (rtl8181 project) */ - write_sa2400(dev, 6, 0x13ff | (1 << 23)); /* MANRX */ - write_sa2400(dev, 8, 0); /* VCO */ - - if (analogphy) { - rtl8180_set_anaparam(priv, anaparam | - (1 << ANAPARAM_TXDACOFF_SHIFT)); - - txconf = rtl818x_ioread32(priv, &priv->map->TX_CONF); - rtl818x_iowrite32(priv, &priv->map->TX_CONF, - txconf | RTL818X_TX_CONF_LOOPBACK_CONT); - - write_sa2400(dev, 4, 0x19341); /* calibrates DC */ - - /* a 5us sleep is required here, - * we rely on the 3ms delay introduced in write_sa2400 */ - write_sa2400(dev, 4, 0x19345); - - /* a 20us sleep is required here, - * we rely on the 3ms delay introduced in write_sa2400 */ - - rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf); - - rtl8180_set_anaparam(priv, anaparam); - } - /* end new code */ - - write_sa2400(dev, 4, 0x19341 | firdac); /* RTX MODE */ - - /* baseband configuration */ - rtl8180_write_phy(dev, 0, 0x98); - rtl8180_write_phy(dev, 3, 0x38); - rtl8180_write_phy(dev, 4, 0xe0); - rtl8180_write_phy(dev, 5, 0x90); - rtl8180_write_phy(dev, 6, 0x1a); - rtl8180_write_phy(dev, 7, 0x64); - - sa2400_write_phy_antenna(dev, 1); - - rtl8180_write_phy(dev, 0x11, 0x80); - - if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & - RTL818X_CONFIG2_ANTENNA_DIV) - rtl8180_write_phy(dev, 0x12, 0xc7); /* enable ant diversity */ - else - rtl8180_write_phy(dev, 0x12, 0x47); /* disable ant diversity */ - - rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); - - rtl8180_write_phy(dev, 0x19, 0x0); - rtl8180_write_phy(dev, 0x1a, 0xa0); -} - -const struct rtl818x_rf_ops sa2400_rf_ops = { - .name = "Philips", - .init = sa2400_rf_init, - .stop = sa2400_rf_stop, - .set_chan = sa2400_rf_set_channel, - .calc_rssi = sa2400_rf_calc_rssi, -}; diff --git a/drivers/net/wireless/rtl818x/rtl8180_sa2400.h b/drivers/net/wireless/rtl818x/rtl8180_sa2400.h deleted file mode 100644 index a4aaa0d413f1..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_sa2400.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef RTL8180_SA2400_H -#define RTL8180_SA2400_H - -/* - * Radio tuning for Philips SA2400 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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. - */ - -#define SA2400_ANTENNA 0x91 -#define SA2400_DIG_ANAPARAM_PWR1_ON 0x8 -#define SA2400_ANA_ANAPARAM_PWR1_ON 0x28 -#define SA2400_ANAPARAM_PWR0_ON 0x3 - -/* RX sensitivity in dbm */ -#define SA2400_MAX_SENS 85 - -#define SA2400_REG4_FIRDAC_SHIFT 7 - -extern const struct rtl818x_rf_ops sa2400_rf_ops; - -#endif /* RTL8180_SA2400_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h deleted file mode 100644 index 98878160a65a..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Definitions for RTL8187 hardware - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * 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. - */ - -#ifndef RTL8187_H -#define RTL8187_H - -#include "rtl818x.h" -#include "rtl8187_leds.h" - -#define RTL8187_EEPROM_TXPWR_BASE 0x05 -#define RTL8187_EEPROM_MAC_ADDR 0x07 -#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ -#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ -#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ -#define RTL8187_EEPROM_SELECT_GPIO 0x3B - -#define RTL8187_REQT_READ 0xC0 -#define RTL8187_REQT_WRITE 0x40 -#define RTL8187_REQ_GET_REG 0x05 -#define RTL8187_REQ_SET_REG 0x05 - -#define RTL8187_MAX_RX 0x9C4 - -#define RFKILL_MASK_8187_89_97 0x2 -#define RFKILL_MASK_8198 0x4 - -struct rtl8187_rx_info { - struct urb *urb; - struct ieee80211_hw *dev; -}; - -struct rtl8187_rx_hdr { - __le32 flags; - u8 noise; - u8 signal; - u8 agc; - u8 reserved; - __le64 mac_time; -} __packed; - -struct rtl8187b_rx_hdr { - __le32 flags; - __le64 mac_time; - u8 sq; - u8 rssi; - u8 agc; - u8 flags2; - __le16 snr_long2end; - s8 pwdb_g12; - u8 fot; -} __packed; - -/* {rtl8187,rtl8187b}_tx_info is in skb */ - -struct rtl8187_tx_hdr { - __le32 flags; - __le16 rts_duration; - __le16 len; - __le32 retry; -} __packed; - -struct rtl8187b_tx_hdr { - __le32 flags; - __le16 rts_duration; - __le16 len; - __le32 unused_1; - __le16 unused_2; - __le16 tx_duration; - __le32 unused_3; - __le32 retry; - __le32 unused_4[2]; -} __packed; - -enum { - DEVICE_RTL8187, - DEVICE_RTL8187B -}; - -struct rtl8187_priv { - /* common between rtl818x drivers */ - struct rtl818x_csr *map; - const struct rtl818x_rf_ops *rf; - struct ieee80211_vif *vif; - - /* The mutex protects the TX loopback state. - * Any attempt to set channels concurrently locks the device. - */ - struct mutex conf_mutex; - - /* rtl8187 specific */ - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; - struct ieee80211_supported_band band; - struct usb_device *udev; - u32 rx_conf; - struct usb_anchor anchored; - struct delayed_work work; - struct ieee80211_hw *dev; -#ifdef CONFIG_RTL8187_LEDS - struct rtl8187_led led_radio; - struct rtl8187_led led_tx; - struct rtl8187_led led_rx; - struct delayed_work led_on; - struct delayed_work led_off; -#endif - u16 txpwr_base; - u8 asic_rev; - u8 is_rtl8187b; - enum { - RTL8187BvB, - RTL8187BvD, - RTL8187BvE - } hw_rev; - struct sk_buff_head rx_queue; - u8 signal; - u8 noise; - u8 slot_time; - u8 aifsn[4]; - u8 rfkill_mask; - struct { - __le64 buf; - struct sk_buff_head queue; - } b_tx_status; /* This queue is used by both -b and non-b devices */ - struct mutex io_mutex; - union { - u8 bits8; - __le16 bits16; - __le32 bits32; - } *io_dmabuf; - bool rfkill_off; -}; - -void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); - -static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv, - u8 *addr, u8 idx) -{ - u8 val; - - mutex_lock(&priv->io_mutex); - usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), - RTL8187_REQ_GET_REG, RTL8187_REQT_READ, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); - - val = priv->io_dmabuf->bits8; - mutex_unlock(&priv->io_mutex); - - return val; -} - -static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr) -{ - return rtl818x_ioread8_idx(priv, addr, 0); -} - -static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv, - __le16 *addr, u8 idx) -{ - __le16 val; - - mutex_lock(&priv->io_mutex); - usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), - RTL8187_REQ_GET_REG, RTL8187_REQT_READ, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); - - val = priv->io_dmabuf->bits16; - mutex_unlock(&priv->io_mutex); - - return le16_to_cpu(val); -} - -static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr) -{ - return rtl818x_ioread16_idx(priv, addr, 0); -} - -static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv, - __le32 *addr, u8 idx) -{ - __le32 val; - - mutex_lock(&priv->io_mutex); - usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), - RTL8187_REQ_GET_REG, RTL8187_REQT_READ, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); - - val = priv->io_dmabuf->bits32; - mutex_unlock(&priv->io_mutex); - - return le32_to_cpu(val); -} - -static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr) -{ - return rtl818x_ioread32_idx(priv, addr, 0); -} - -static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv, - u8 *addr, u8 val, u8 idx) -{ - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits8 = val; - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); - - mutex_unlock(&priv->io_mutex); -} - -static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val) -{ - rtl818x_iowrite8_idx(priv, addr, val, 0); -} - -static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv, - __le16 *addr, u16 val, u8 idx) -{ - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits16 = cpu_to_le16(val); - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); - - mutex_unlock(&priv->io_mutex); -} - -static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr, - u16 val) -{ - rtl818x_iowrite16_idx(priv, addr, val, 0); -} - -static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv, - __le32 *addr, u32 val, u8 idx) -{ - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits32 = cpu_to_le32(val); - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); - - mutex_unlock(&priv->io_mutex); -} - -static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr, - u32 val) -{ - rtl818x_iowrite32_idx(priv, addr, val, 0); -} - -#endif /* RTL8187_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/Makefile b/drivers/net/wireless/rtl818x/rtl8187/Makefile new file mode 100644 index 000000000000..7b6299268ecf --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/Makefile @@ -0,0 +1,5 @@ +rtl8187-objs := dev.o rtl8225.o leds.o rfkill.o + +obj-$(CONFIG_RTL8187) += rtl8187.o + +ccflags-y += -Idrivers/net/wireless/rtl818x diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c new file mode 100644 index 000000000000..6b82cac37ee3 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -0,0 +1,1591 @@ +/* + * Linux device driver for RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * The driver was extended to the RTL8187B in 2008 by: + * Herton Ronaldo Krzesinski + * Hin-Tak Leung + * Larry Finger + * + * Magic delays and register offsets below are taken from the original + * r8187 driver sources. Thanks to Realtek for their support! + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "rtl8187.h" +#include "rtl8225.h" +#ifdef CONFIG_RTL8187_LEDS +#include "leds.h" +#endif +#include "rfkill.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_AUTHOR("Andrea Merello "); +MODULE_AUTHOR("Herton Ronaldo Krzesinski "); +MODULE_AUTHOR("Hin-Tak Leung "); +MODULE_AUTHOR("Larry Finger "); +MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); +MODULE_LICENSE("GPL"); + +static struct usb_device_id rtl8187_table[] __devinitdata = { + /* Asus */ + {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, + /* Belkin */ + {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B}, + /* Realtek */ + {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, + {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B}, + {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B}, + /* Surecom */ + {USB_DEVICE(0x0769, 0x11F2), .driver_info = DEVICE_RTL8187}, + /* Logitech */ + {USB_DEVICE(0x0789, 0x010C), .driver_info = DEVICE_RTL8187}, + /* Netgear */ + {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, + /* HP */ + {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, + /* Sitecom */ + {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, + {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B}, + /* Sphairon Access Systems GmbH */ + {USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187}, + /* Dick Smith Electronics */ + {USB_DEVICE(0x1371, 0x9401), .driver_info = DEVICE_RTL8187}, + /* Abocom */ + {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, + /* Qcom */ + {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, + /* AirLive */ + {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, + /* Linksys */ + {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, + {} +}; + +MODULE_DEVICE_TABLE(usb, rtl8187_table); + +static const struct ieee80211_rate rtl818x_rates[] = { + { .bitrate = 10, .hw_value = 0, }, + { .bitrate = 20, .hw_value = 1, }, + { .bitrate = 55, .hw_value = 2, }, + { .bitrate = 110, .hw_value = 3, }, + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static const struct ieee80211_channel rtl818x_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + +static void rtl8187_iowrite_async_cb(struct urb *urb) +{ + kfree(urb->context); +} + +static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, + void *data, u16 len) +{ + struct usb_ctrlrequest *dr; + struct urb *urb; + struct rtl8187_async_write_data { + u8 data[4]; + struct usb_ctrlrequest dr; + } *buf; + int rc; + + buf = kmalloc(sizeof(*buf), GFP_ATOMIC); + if (!buf) + return; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree(buf); + return; + } + + dr = &buf->dr; + + dr->bRequestType = RTL8187_REQT_WRITE; + dr->bRequest = RTL8187_REQ_SET_REG; + dr->wValue = addr; + dr->wIndex = 0; + dr->wLength = cpu_to_le16(len); + + memcpy(buf, data, len); + + usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), + (unsigned char *)dr, buf, len, + rtl8187_iowrite_async_cb, buf); + usb_anchor_urb(urb, &priv->anchored); + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc < 0) { + kfree(buf); + usb_unanchor_urb(urb); + } + usb_free_urb(urb); +} + +static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, + __le32 *addr, u32 val) +{ + __le32 buf = cpu_to_le32(val); + + rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr), + &buf, sizeof(buf)); +} + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8187_priv *priv = dev->priv; + + data <<= 8; + data |= addr | 0x80; + + rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); +} + +static void rtl8187_tx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *)urb->context; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hw *hw = info->rate_driver_data[0]; + struct rtl8187_priv *priv = hw->priv; + + skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : + sizeof(struct rtl8187_tx_hdr)); + ieee80211_tx_info_clear_status(info); + + if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + if (priv->is_rtl8187b) { + skb_queue_tail(&priv->b_tx_status.queue, skb); + + /* queue is "full", discard last items */ + while (skb_queue_len(&priv->b_tx_status.queue) > 5) { + struct sk_buff *old_skb; + + dev_dbg(&priv->udev->dev, + "transmit status queue full\n"); + + old_skb = skb_dequeue(&priv->b_tx_status.queue); + ieee80211_tx_status_irqsafe(hw, old_skb); + } + return; + } else { + info->flags |= IEEE80211_TX_STAT_ACK; + } + } + if (priv->is_rtl8187b) + ieee80211_tx_status_irqsafe(hw, skb); + else { + /* Retry information for the RTI8187 is only available by + * reading a register in the device. We are in interrupt mode + * here, thus queue the skb and finish on a work queue. */ + skb_queue_tail(&priv->b_tx_status.queue, skb); + ieee80211_queue_delayed_work(hw, &priv->work, 0); + } +} + +static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct rtl8187_priv *priv = dev->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned int ep; + void *buf; + struct urb *urb; + __le16 rts_dur = 0; + u32 flags; + int rc; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + flags = skb->len; + flags |= RTL818X_TX_DESC_FLAG_NO_ENC; + + flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; + if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) + flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { + flags |= RTL818X_TX_DESC_FLAG_RTS; + flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_dur = ieee80211_rts_duration(dev, priv->vif, + skb->len, info); + } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + flags |= RTL818X_TX_DESC_FLAG_CTS; + flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + } + + if (!priv->is_rtl8187b) { + struct rtl8187_tx_hdr *hdr = + (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr)); + hdr->flags = cpu_to_le32(flags); + hdr->len = 0; + hdr->rts_duration = rts_dur; + hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); + buf = hdr; + + ep = 2; + } else { + /* fc needs to be calculated before skb_push() */ + unsigned int epmap[4] = { 6, 7, 5, 4 }; + struct ieee80211_hdr *tx_hdr = + (struct ieee80211_hdr *)(skb->data); + u16 fc = le16_to_cpu(tx_hdr->frame_control); + + struct rtl8187b_tx_hdr *hdr = + (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr)); + struct ieee80211_rate *txrate = + ieee80211_get_tx_rate(dev, info); + memset(hdr, 0, sizeof(*hdr)); + hdr->flags = cpu_to_le32(flags); + hdr->rts_duration = rts_dur; + hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); + hdr->tx_duration = + ieee80211_generic_frame_duration(dev, priv->vif, + skb->len, txrate); + buf = hdr; + + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + ep = 12; + else + ep = epmap[skb_get_queue_mapping(skb)]; + } + + info->rate_driver_data[0] = dev; + info->rate_driver_data[1] = urb; + + usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), + buf, skb->len, rtl8187_tx_cb, skb); + urb->transfer_flags |= URB_ZERO_PACKET; + usb_anchor_urb(urb, &priv->anchored); + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc < 0) { + usb_unanchor_urb(urb); + kfree_skb(skb); + } + usb_free_urb(urb); + + return NETDEV_TX_OK; +} + +static void rtl8187_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *)urb->context; + struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; + struct ieee80211_hw *dev = info->dev; + struct rtl8187_priv *priv = dev->priv; + struct ieee80211_rx_status rx_status = { 0 }; + int rate, signal; + u32 flags; + unsigned long f; + + spin_lock_irqsave(&priv->rx_queue.lock, f); + __skb_unlink(skb, &priv->rx_queue); + spin_unlock_irqrestore(&priv->rx_queue.lock, f); + skb_put(skb, urb->actual_length); + + if (unlikely(urb->status)) { + dev_kfree_skb_irq(skb); + return; + } + + if (!priv->is_rtl8187b) { + struct rtl8187_rx_hdr *hdr = + (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + flags = le32_to_cpu(hdr->flags); + /* As with the RTL8187B below, the AGC is used to calculate + * signal strength. In this case, the scaling + * constants are derived from the output of p54usb. + */ + signal = -4 - ((27 * hdr->agc) >> 6); + rx_status.antenna = (hdr->signal >> 7) & 1; + rx_status.mactime = le64_to_cpu(hdr->mac_time); + } else { + struct rtl8187b_rx_hdr *hdr = + (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + /* The Realtek datasheet for the RTL8187B shows that the RX + * header contains the following quantities: signal quality, + * RSSI, AGC, the received power in dB, and the measured SNR. + * In testing, none of these quantities show qualitative + * agreement with AP signal strength, except for the AGC, + * which is inversely proportional to the strength of the + * signal. In the following, the signal strength + * is derived from the AGC. The arbitrary scaling constants + * are chosen to make the results close to the values obtained + * for a BCM4312 using b43 as the driver. The noise is ignored + * for now. + */ + flags = le32_to_cpu(hdr->flags); + signal = 14 - hdr->agc / 2; + rx_status.antenna = (hdr->rssi >> 7) & 1; + rx_status.mactime = le64_to_cpu(hdr->mac_time); + } + + rx_status.signal = signal; + priv->signal = signal; + rate = (flags >> 20) & 0xF; + skb_trim(skb, flags & 0x0FFF); + rx_status.rate_idx = rate; + rx_status.freq = dev->conf.channel->center_freq; + rx_status.band = dev->conf.channel->band; + rx_status.flag |= RX_FLAG_TSFT; + if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); + + skb = dev_alloc_skb(RTL8187_MAX_RX); + if (unlikely(!skb)) { + /* TODO check rx queue length and refill *somewhere* */ + return; + } + + info = (struct rtl8187_rx_info *)skb->cb; + info->urb = urb; + info->dev = dev; + urb->transfer_buffer = skb_tail_pointer(skb); + urb->context = skb; + skb_queue_tail(&priv->rx_queue, skb); + + usb_anchor_urb(urb, &priv->anchored); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + usb_unanchor_urb(urb); + skb_unlink(skb, &priv->rx_queue); + dev_kfree_skb_irq(skb); + } +} + +static int rtl8187_init_urbs(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct urb *entry = NULL; + struct sk_buff *skb; + struct rtl8187_rx_info *info; + int ret = 0; + + while (skb_queue_len(&priv->rx_queue) < 16) { + skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); + if (!skb) { + ret = -ENOMEM; + goto err; + } + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) { + ret = -ENOMEM; + goto err; + } + usb_fill_bulk_urb(entry, priv->udev, + usb_rcvbulkpipe(priv->udev, + priv->is_rtl8187b ? 3 : 1), + skb_tail_pointer(skb), + RTL8187_MAX_RX, rtl8187_rx_cb, skb); + info = (struct rtl8187_rx_info *)skb->cb; + info->urb = entry; + info->dev = dev; + skb_queue_tail(&priv->rx_queue, skb); + usb_anchor_urb(entry, &priv->anchored); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(entry); + goto err; + } + usb_free_urb(entry); + } + return ret; + +err: + usb_free_urb(entry); + kfree_skb(skb); + usb_kill_anchored_urbs(&priv->anchored); + return ret; +} + +static void rtl8187b_status_cb(struct urb *urb) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; + struct rtl8187_priv *priv = hw->priv; + u64 val; + unsigned int cmd_type; + + if (unlikely(urb->status)) + return; + + /* + * Read from status buffer: + * + * bits [30:31] = cmd type: + * - 0 indicates tx beacon interrupt + * - 1 indicates tx close descriptor + * + * In the case of tx beacon interrupt: + * [0:9] = Last Beacon CW + * [10:29] = reserved + * [30:31] = 00b + * [32:63] = Last Beacon TSF + * + * If it's tx close descriptor: + * [0:7] = Packet Retry Count + * [8:14] = RTS Retry Count + * [15] = TOK + * [16:27] = Sequence No + * [28] = LS + * [29] = FS + * [30:31] = 01b + * [32:47] = unused (reserved?) + * [48:63] = MAC Used Time + */ + val = le64_to_cpu(priv->b_tx_status.buf); + + cmd_type = (val >> 30) & 0x3; + if (cmd_type == 1) { + unsigned int pkt_rc, seq_no; + bool tok; + struct sk_buff *skb; + struct ieee80211_hdr *ieee80211hdr; + unsigned long flags; + + pkt_rc = val & 0xFF; + tok = val & (1 << 15); + seq_no = (val >> 16) & 0xFFF; + + spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); + skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { + ieee80211hdr = (struct ieee80211_hdr *)skb->data; + + /* + * While testing, it was discovered that the seq_no + * doesn't actually contains the sequence number. + * Instead of returning just the 12 bits of sequence + * number, hardware is returning entire sequence control + * (fragment number plus sequence number) in a 12 bit + * only field overflowing after some time. As a + * workaround, just consider the lower bits, and expect + * it's unlikely we wrongly ack some sent data + */ + if ((le16_to_cpu(ieee80211hdr->seq_ctrl) + & 0xFFF) == seq_no) + break; + } + if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + __skb_unlink(skb, &priv->b_tx_status.queue); + if (tok) + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.rates[0].count = pkt_rc + 1; + + ieee80211_tx_status_irqsafe(hw, skb); + } + spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); + } + + usb_anchor_urb(urb, &priv->anchored); + if (usb_submit_urb(urb, GFP_ATOMIC)) + usb_unanchor_urb(urb); +} + +static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct urb *entry; + int ret = 0; + + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) + return -ENOMEM; + + usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), + &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), + rtl8187b_status_cb, dev); + + usb_anchor_urb(entry, &priv->anchored); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) + usb_unanchor_urb(entry); + usb_free_urb(entry); + + return ret; +} + +static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) +{ + u32 anaparam, anaparam2; + u8 anaparam3, reg; + + if (!priv->is_rtl8187b) { + if (rfon) { + anaparam = RTL8187_RTL8225_ANAPARAM_ON; + anaparam2 = RTL8187_RTL8225_ANAPARAM2_ON; + } else { + anaparam = RTL8187_RTL8225_ANAPARAM_OFF; + anaparam2 = RTL8187_RTL8225_ANAPARAM2_OFF; + } + } else { + if (rfon) { + anaparam = RTL8187B_RTL8225_ANAPARAM_ON; + anaparam2 = RTL8187B_RTL8225_ANAPARAM2_ON; + anaparam3 = RTL8187B_RTL8225_ANAPARAM3_ON; + } else { + anaparam = RTL8187B_RTL8225_ANAPARAM_OFF; + anaparam2 = RTL8187B_RTL8225_ANAPARAM2_OFF; + anaparam3 = RTL8187B_RTL8225_ANAPARAM3_OFF; + } + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + reg |= RTL818X_CONFIG3_ANAPARAM_WRITE; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); + if (priv->is_rtl8187b) + rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3); + reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); +} + +static int rtl8187_cmd_reset(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u8 reg; + int i; + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= (1 << 1); + reg |= RTL818X_CMD_RESET; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + i = 10; + do { + msleep(2); + if (!(rtl818x_ioread8(priv, &priv->map->CMD) & + RTL818X_CMD_RESET)) + break; + } while (--i); + + if (!i) { + wiphy_err(dev->wiphy, "Reset timeout!\n"); + return -ETIMEDOUT; + } + + /* reload registers from eeprom */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); + + i = 10; + do { + msleep(4); + if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) & + RTL818X_EEPROM_CMD_CONFIG)) + break; + } while (--i); + + if (!i) { + wiphy_err(dev->wiphy, "eeprom reset timeout!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rtl8187_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u8 reg; + int res; + + /* reset */ + rtl8187_set_anaparam(priv, true); + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + + msleep(200); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); + msleep(200); + + res = rtl8187_cmd_reset(dev); + if (res) + return res; + + rtl8187_set_anaparam(priv, true); + + /* setup card */ + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); + + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + + rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); + reg &= 0x3F; + reg |= 0x80; + rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); + + // TODO: set RESP_RATE and BRSR properly + rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + + /* host_usb_init */ + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); + reg = rtl818x_ioread8(priv, (u8 *)0xFE53); + rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); + msleep(100); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); + msleep(100); + + priv->rf->init(dev); + + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); + rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); + rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); + rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + + return 0; +} + +static const u8 rtl8187b_reg_table[][3] = { + {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0}, + {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0}, + {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0}, + {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0}, + + {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1}, + {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1}, + {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, + {0xF2, 0x02, 1}, {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, + {0xF6, 0x06, 1}, {0xF7, 0x07, 1}, {0xF8, 0x08, 1}, + + {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2}, + {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2}, + {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2}, + {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2}, + {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2}, + {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2}, + {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, + + {0x5B, 0x40, 0}, {0x84, 0x88, 0}, {0x85, 0x24, 0}, {0x88, 0x54, 0}, + {0x8B, 0xB8, 0}, {0x8C, 0x07, 0}, {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, + {0x95, 0x12, 0}, {0x96, 0x00, 0}, {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, + {0x9F, 0x10, 0}, {0xB4, 0x22, 0}, {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, + {0xEE, 0x00, 0}, {0x4C, 0x00, 2}, + + {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0}, {0x8E, 0x08, 0}, + {0x8F, 0x00, 0} +}; + +static int rtl8187b_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int res, i; + u8 reg; + + rtl8187_set_anaparam(priv, true); + + /* Reset PLL sequence on 8187B. Realtek note: reduces power + * consumption about 30 mA */ + rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10); + reg = rtl818x_ioread8(priv, (u8 *)0xFF62); + rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5)); + rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5)); + + res = rtl8187_cmd_reset(dev); + if (res) + return res; + + rtl8187_set_anaparam(priv, true); + + /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as + * RESP_RATE on 8187L in Realtek sources: each bit should be each + * one of the 12 rates, all are enabled */ + rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); + + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ + rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); + rtl818x_iowrite8_idx(priv, (u8 *)0xFFE2, 0x00, 1); + + rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); + rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { + rtl818x_iowrite8_idx(priv, + (u8 *)(uintptr_t) + (rtl8187b_reg_table[i][0] | 0xFF00), + rtl8187b_reg_table[i][1], + rtl8187b_reg_table[i][2]); + } + + rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); + rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); + + rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1); + rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1); + rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001); + + /* RFSW_CTRL register */ + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + msleep(100); + + priv->rf->init(dev); + + reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + + rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4); + rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); + rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); + + reg = rtl818x_ioread8(priv, (u8 *)0xFFDB); + rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2)); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3); + rtl818x_iowrite8(priv, (u8 *)0xFF61, 0); + rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1); + rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1); + rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10); + rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2); + + rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B); + + rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); + + priv->slot_time = 0x9; + priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ + priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ + priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ + priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ + rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); + + /* ENEDCA flag must always be set, transmit issues? */ + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); + + return 0; +} + +static void rtl8187_work(struct work_struct *work) +{ + /* The RTL8187 returns the retry count through register 0xFFFA. In + * addition, it appears to be a cumulative retry count, not the + * value for the current TX packet. When multiple TX entries are + * queued, the retry count will be valid for the last one in the queue. + * The "error" should not matter for purposes of rate setting. */ + struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, + work.work); + struct ieee80211_tx_info *info; + struct ieee80211_hw *dev = priv->dev; + static u16 retry; + u16 tmp; + + mutex_lock(&priv->conf_mutex); + tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); + while (skb_queue_len(&priv->b_tx_status.queue) > 0) { + struct sk_buff *old_skb; + + old_skb = skb_dequeue(&priv->b_tx_status.queue); + info = IEEE80211_SKB_CB(old_skb); + info->status.rates[0].count = tmp - retry + 1; + ieee80211_tx_status_irqsafe(dev, old_skb); + } + retry = tmp; + mutex_unlock(&priv->conf_mutex); +} + +static int rtl8187_start(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u32 reg; + int ret; + + mutex_lock(&priv->conf_mutex); + + ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : + rtl8187b_init_hw(dev); + if (ret) + goto rtl8187_start_exit; + + init_usb_anchor(&priv->anchored); + priv->dev = dev; + + if (priv->is_rtl8187b) { + reg = RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_DATA | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_NICMAC | + RTL818X_RX_CONF_BSSID | + (7 << 13 /* RX FIFO threshold NONE */) | + (7 << 10 /* MAX RX DMA */) | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_MULTICAST; + priv->rx_conf = reg; + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + RTL818X_TX_CONF_HW_SEQNUM | + RTL818X_TX_CONF_DISREQQSIZE | + (7 << 8 /* short retry limit */) | + (7 << 0 /* long retry limit */) | + (7 << 21 /* MAX TX DMA */)); + rtl8187_init_urbs(dev); + rtl8187b_init_status_urb(dev); + goto rtl8187_start_exit; + } + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + + rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); + rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); + + rtl8187_init_urbs(dev); + + reg = RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_BSSID | + RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_DATA | + (7 << 13 /* RX FIFO threshold NONE */) | + (7 << 10 /* MAX RX DMA */) | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_NICMAC; + + priv->rx_conf = reg; + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + reg = RTL818X_TX_CONF_CW_MIN | + (7 << 21 /* MAX TX DMA */) | + RTL818X_TX_CONF_NO_ICV; + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg |= RTL818X_CMD_TX_ENABLE; + reg |= RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + INIT_DELAYED_WORK(&priv->work, rtl8187_work); + +rtl8187_start_exit: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void rtl8187_stop(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct sk_buff *skb; + u32 reg; + + mutex_lock(&priv->conf_mutex); + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= ~RTL818X_CMD_TX_ENABLE; + reg &= ~RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + priv->rf->stop(dev); + rtl8187_set_anaparam(priv, false); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); + rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + while ((skb = skb_dequeue(&priv->b_tx_status.queue))) + dev_kfree_skb_any(skb); + + usb_kill_anchored_urbs(&priv->anchored); + mutex_unlock(&priv->conf_mutex); + + if (!priv->is_rtl8187b) + cancel_delayed_work_sync(&priv->work); +} + +static int rtl8187_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + int ret = -EOPNOTSUPP; + + mutex_lock(&priv->conf_mutex); + if (priv->vif) + goto exit; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + break; + default: + goto exit; + } + + ret = 0; + priv->vif = vif; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->MAC[i], + ((u8 *)vif->addr)[i]); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + +exit: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void rtl8187_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8187_priv *priv = dev->priv; + mutex_lock(&priv->conf_mutex); + priv->vif = NULL; + mutex_unlock(&priv->conf_mutex); +} + +static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) +{ + struct rtl8187_priv *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + u32 reg; + + mutex_lock(&priv->conf_mutex); + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + /* Enable TX loopback on MAC level to avoid TX during channel + * changes, as this has be seen to causes problems and the + * card will stop work until next reset + */ + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + reg | RTL818X_TX_CONF_LOOPBACK_MAC); + priv->rf->set_chan(dev, conf); + msleep(10); + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); + rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); + rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); + rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); + mutex_unlock(&priv->conf_mutex); + return 0; +} + +/* + * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for + * example. Thus we have to use raw values for AC_*_PARAM register addresses. + */ +static __le32 *rtl8187b_ac_addr[4] = { + (__le32 *) 0xFFF0, /* AC_VO */ + (__le32 *) 0xFFF4, /* AC_VI */ + (__le32 *) 0xFFFC, /* AC_BK */ + (__le32 *) 0xFFF8, /* AC_BE */ +}; + +#define SIFS_TIME 0xa + +static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, + bool use_short_preamble) +{ + if (priv->is_rtl8187b) { + u8 difs, eifs; + u16 ack_timeout; + int queue; + + if (use_short_slot) { + priv->slot_time = 0x9; + difs = 0x1c; + eifs = 0x53; + } else { + priv->slot_time = 0x14; + difs = 0x32; + eifs = 0x5b; + } + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); + rtl818x_iowrite8(priv, &priv->map->DIFS, difs); + + /* + * BRSR+1 on 8187B is in fact EIFS register + * Value in units of 4 us + */ + rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs); + + /* + * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout + * register. In units of 4 us like eifs register + * ack_timeout = ack duration + plcp + difs + preamble + */ + ack_timeout = 112 + 48 + difs; + if (use_short_preamble) + ack_timeout += 72; + else + ack_timeout += 144; + rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, + DIV_ROUND_UP(ack_timeout, 4)); + + for (queue = 0; queue < 4; queue++) + rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], + priv->aifsn[queue] * priv->slot_time + + SIFS_TIME); + } else { + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + if (use_short_slot) { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); + rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); + } else { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); + rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); + } + } +} + +static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + u8 reg; + + if (changed & BSS_CHANGED_BSSID) { + mutex_lock(&priv->conf_mutex); + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], + info->bssid[i]); + + if (priv->is_rtl8187b) + reg = RTL818X_MSR_ENEDCA; + else + reg = 0; + + if (is_valid_ether_addr(info->bssid)) + reg |= RTL818X_MSR_INFRA; + else + reg |= RTL818X_MSR_NO_LINK; + + rtl818x_iowrite8(priv, &priv->map->MSR, reg); + + mutex_unlock(&priv->conf_mutex); + } + + if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) + rtl8187_conf_erp(priv, info->use_short_slot, + info->use_short_preamble); +} + +static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, + struct netdev_hw_addr_list *mc_list) +{ + return netdev_hw_addr_list_count(mc_list); +} + +static void rtl8187_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct rtl8187_priv *priv = dev->priv; + + if (changed_flags & FIF_FCSFAIL) + priv->rx_conf ^= RTL818X_RX_CONF_FCS; + if (changed_flags & FIF_CONTROL) + priv->rx_conf ^= RTL818X_RX_CONF_CTRL; + if (changed_flags & FIF_OTHER_BSS) + priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; + if (*total_flags & FIF_ALLMULTI || multicast > 0) + priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; + else + priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; + + *total_flags = 0; + + if (priv->rx_conf & RTL818X_RX_CONF_FCS) + *total_flags |= FIF_FCSFAIL; + if (priv->rx_conf & RTL818X_RX_CONF_CTRL) + *total_flags |= FIF_CONTROL; + if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) + *total_flags |= FIF_OTHER_BSS; + if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) + *total_flags |= FIF_ALLMULTI; + + rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); +} + +static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cw_min, cw_max; + + if (queue > 3) + return -EINVAL; + + cw_min = fls(params->cw_min); + cw_max = fls(params->cw_max); + + if (priv->is_rtl8187b) { + priv->aifsn[queue] = params->aifs; + + /* + * This is the structure of AC_*_PARAM registers in 8187B: + * - TXOP limit field, bit offset = 16 + * - ECWmax, bit offset = 12 + * - ECWmin, bit offset = 8 + * - AIFS, bit offset = 0 + */ + rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], + (params->txop << 16) | (cw_max << 12) | + (cw_min << 8) | (params->aifs * + priv->slot_time + SIFS_TIME)); + } else { + if (queue != 0) + return -EINVAL; + + rtl818x_iowrite8(priv, &priv->map->CW_VAL, + cw_min | (cw_max << 4)); + } + return 0; +} + +static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + + return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | + (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; +} + +static const struct ieee80211_ops rtl8187_ops = { + .tx = rtl8187_tx, + .start = rtl8187_start, + .stop = rtl8187_stop, + .add_interface = rtl8187_add_interface, + .remove_interface = rtl8187_remove_interface, + .config = rtl8187_config, + .bss_info_changed = rtl8187_bss_info_changed, + .prepare_multicast = rtl8187_prepare_multicast, + .configure_filter = rtl8187_configure_filter, + .conf_tx = rtl8187_conf_tx, + .rfkill_poll = rtl8187_rfkill_poll, + .get_tsf = rtl8187_get_tsf, +}; + +static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8187_priv *priv = dev->priv; + u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + + eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; + eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; + eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; + eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; +} + +static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8187_priv *priv = dev->priv; + u8 reg = RTL818X_EEPROM_CMD_PROGRAM; + + if (eeprom->reg_data_in) + reg |= RTL818X_EEPROM_CMD_WRITE; + if (eeprom->reg_data_out) + reg |= RTL818X_EEPROM_CMD_READ; + if (eeprom->reg_data_clock) + reg |= RTL818X_EEPROM_CMD_CK; + if (eeprom->reg_chip_select) + reg |= RTL818X_EEPROM_CMD_CS; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); + udelay(10); +} + +static int __devinit rtl8187_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ieee80211_hw *dev; + struct rtl8187_priv *priv; + struct eeprom_93cx6 eeprom; + struct ieee80211_channel *channel; + const char *chip_name; + u16 txpwr, reg; + u16 product_id = le16_to_cpu(udev->descriptor.idProduct); + int err, i; + u8 mac_addr[ETH_ALEN]; + + dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); + if (!dev) { + printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); + return -ENOMEM; + } + + priv = dev->priv; + priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B); + + /* allocate "DMA aware" buffer for register accesses */ + priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL); + if (!priv->io_dmabuf) { + err = -ENOMEM; + goto err_free_dev; + } + mutex_init(&priv->io_mutex); + + SET_IEEE80211_DEV(dev, &intf->dev); + usb_set_intfdata(intf, dev); + priv->udev = udev; + + usb_get_dev(udev); + + skb_queue_head_init(&priv->rx_queue); + + BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); + BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); + + memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); + memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); + priv->map = (struct rtl818x_csr *)0xFF00; + + priv->band.band = IEEE80211_BAND_2GHZ; + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + + + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_RX_INCLUDES_FCS; + + eeprom.data = dev; + eeprom.register_read = rtl8187_eeprom_register_read; + eeprom.register_write = rtl8187_eeprom_register_write; + if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) + eeprom.width = PCI_EEPROM_WIDTH_93C66; + else + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + udelay(10); + + eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR, + (__le16 __force *)mac_addr, 3); + if (!is_valid_ether_addr(mac_addr)) { + printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly " + "generated MAC address\n"); + random_ether_addr(mac_addr); + } + SET_IEEE80211_PERM_ADDR(dev, mac_addr); + + channel = priv->channels; + for (i = 0; i < 3; i++) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + for (i = 0; i < 2; i++) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, + &priv->txpwr_base); + + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); + /* 0 means asic B-cut, we should use SW 3 wire + * bit-by-bit banging for radio. 1 means we can use + * USB specific request to write radio registers */ + priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3; + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + if (!priv->is_rtl8187b) { + u32 reg32; + reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF); + reg32 &= RTL818X_TX_CONF_HWVER_MASK; + switch (reg32) { + case RTL818X_TX_CONF_R8187vD_B: + /* Some RTL8187B devices have a USB ID of 0x8187 + * detect them here */ + chip_name = "RTL8187BvB(early)"; + priv->is_rtl8187b = 1; + priv->hw_rev = RTL8187BvB; + break; + case RTL818X_TX_CONF_R8187vD: + chip_name = "RTL8187vD"; + break; + default: + chip_name = "RTL8187vB (default)"; + } + } else { + /* + * Force USB request to write radio registers for 8187B, Realtek + * only uses it in their sources + */ + /*if (priv->asic_rev == 0) { + printk(KERN_WARNING "rtl8187: Forcing use of USB " + "requests to write to radio registers\n"); + priv->asic_rev = 1; + }*/ + switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) { + case RTL818X_R8187B_B: + chip_name = "RTL8187BvB"; + priv->hw_rev = RTL8187BvB; + break; + case RTL818X_R8187B_D: + chip_name = "RTL8187BvD"; + priv->hw_rev = RTL8187BvD; + break; + case RTL818X_R8187B_E: + chip_name = "RTL8187BvE"; + priv->hw_rev = RTL8187BvE; + break; + default: + chip_name = "RTL8187BvB (default)"; + priv->hw_rev = RTL8187BvB; + } + } + + if (!priv->is_rtl8187b) { + for (i = 0; i < 2; i++) { + eeprom_93cx6_read(&eeprom, + RTL8187_EEPROM_TXPWR_CHAN_6 + i, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + } else { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + + eeprom_93cx6_read(&eeprom, 0x0A, &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + + eeprom_93cx6_read(&eeprom, 0x1C, &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + /* Handle the differing rfkill GPIO bit in different models */ + priv->rfkill_mask = RFKILL_MASK_8187_89_97; + if (product_id == 0x8197 || product_id == 0x8198) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); + if (reg & 0xFF00) + priv->rfkill_mask = RFKILL_MASK_8198; + } + + /* + * XXX: Once this driver supports anything that requires + * beacons it must implement IEEE80211_TX_CTL_ASSIGN_SEQ. + */ + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) + printk(KERN_INFO "rtl8187: inconsistency between id with OEM" + " info!\n"); + + priv->rf = rtl8187_detect_rf(dev); + dev->extra_tx_headroom = (!priv->is_rtl8187b) ? + sizeof(struct rtl8187_tx_hdr) : + sizeof(struct rtl8187b_tx_hdr); + if (!priv->is_rtl8187b) + dev->queues = 1; + else + dev->queues = 4; + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "rtl8187: Cannot register device\n"); + goto err_free_dmabuf; + } + mutex_init(&priv->conf_mutex); + skb_queue_head_init(&priv->b_tx_status.queue); + + wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", + mac_addr, chip_name, priv->asic_rev, priv->rf->name, + priv->rfkill_mask); + +#ifdef CONFIG_RTL8187_LEDS + eeprom_93cx6_read(&eeprom, 0x3F, ®); + reg &= 0xFF; + rtl8187_leds_init(dev, reg); +#endif + rtl8187_rfkill_init(dev); + + return 0; + + err_free_dmabuf: + kfree(priv->io_dmabuf); + err_free_dev: + ieee80211_free_hw(dev); + usb_set_intfdata(intf, NULL); + usb_put_dev(udev); + return err; +} + +static void __devexit rtl8187_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct rtl8187_priv *priv; + + if (!dev) + return; + +#ifdef CONFIG_RTL8187_LEDS + rtl8187_leds_exit(dev); +#endif + rtl8187_rfkill_exit(dev); + ieee80211_unregister_hw(dev); + + priv = dev->priv; + usb_reset_device(priv->udev); + usb_put_dev(interface_to_usbdev(intf)); + kfree(priv->io_dmabuf); + ieee80211_free_hw(dev); +} + +static struct usb_driver rtl8187_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8187_table, + .probe = rtl8187_probe, + .disconnect = __devexit_p(rtl8187_disconnect), +}; + +static int __init rtl8187_init(void) +{ + return usb_register(&rtl8187_driver); +} + +static void __exit rtl8187_exit(void) +{ + usb_deregister(&rtl8187_driver); +} + +module_init(rtl8187_init); +module_exit(rtl8187_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8187/leds.c b/drivers/net/wireless/rtl818x/rtl8187/leds.c new file mode 100644 index 000000000000..2e0de2f5f0f9 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/leds.c @@ -0,0 +1,245 @@ +/* + * Linux LED driver for RTL8187 + * + * Copyright 2009 Larry Finger + * + * Based on the LED handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * Thanks to Realtek for their support! + * + * 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. + */ + +#ifdef CONFIG_RTL8187_LEDS + +#include +#include +#include + +#include "rtl8187.h" +#include "leds.h" + +static void led_turn_on(struct work_struct *work) +{ + /* As this routine does read/write operations on the hardware, it must + * be run from a work queue. + */ + u8 reg; + struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, + led_on.work); + struct rtl8187_led *led = &priv->led_tx; + + /* Don't change the LED, when the device is down. */ + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) + return ; + + /* Skip if the LED is not registered. */ + if (!led->dev) + return; + mutex_lock(&priv->conf_mutex); + switch (led->ledpin) { + case LED_PIN_GPIO0: + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); + break; + case LED_PIN_LED0: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_LED1: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_HW: + default: + break; + } + mutex_unlock(&priv->conf_mutex); +} + +static void led_turn_off(struct work_struct *work) +{ + /* As this routine does read/write operations on the hardware, it must + * be run from a work queue. + */ + u8 reg; + struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, + led_off.work); + struct rtl8187_led *led = &priv->led_tx; + + /* Don't change the LED, when the device is down. */ + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) + return ; + + /* Skip if the LED is not registered. */ + if (!led->dev) + return; + mutex_lock(&priv->conf_mutex); + switch (led->ledpin) { + case LED_PIN_GPIO0: + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); + break; + case LED_PIN_LED0: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_LED1: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_HW: + default: + break; + } + mutex_unlock(&priv->conf_mutex); +} + +/* Callback from the LED subsystem. */ +static void rtl8187_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, + led_dev); + struct ieee80211_hw *hw = led->dev; + struct rtl8187_priv *priv; + static bool radio_on; + + if (!hw) + return; + priv = hw->priv; + if (led->is_radio) { + if (brightness == LED_FULL) { + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + radio_on = true; + } else if (radio_on) { + radio_on = false; + cancel_delayed_work_sync(&priv->led_on); + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); + } + } else if (radio_on) { + if (brightness == LED_OFF) { + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); + /* The LED is off for 1/20 sec - it just blinks. */ + ieee80211_queue_delayed_work(hw, &priv->led_on, + HZ / 20); + } else + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + } +} + +static int rtl8187_register_led(struct ieee80211_hw *dev, + struct rtl8187_led *led, const char *name, + const char *default_trigger, u8 ledpin, + bool is_radio) +{ + int err; + struct rtl8187_priv *priv = dev->priv; + + if (led->dev) + return -EEXIST; + if (!default_trigger) + return -EINVAL; + led->dev = dev; + led->ledpin = ledpin; + led->is_radio = is_radio; + strncpy(led->name, name, sizeof(led->name)); + + led->led_dev.name = led->name; + led->led_dev.default_trigger = default_trigger; + led->led_dev.brightness_set = rtl8187_led_brightness_set; + + err = led_classdev_register(&priv->udev->dev, &led->led_dev); + if (err) { + printk(KERN_INFO "LEDs: Failed to register %s\n", name); + led->dev = NULL; + return err; + } + return 0; +} + +static void rtl8187_unregister_led(struct rtl8187_led *led) +{ + struct ieee80211_hw *hw = led->dev; + struct rtl8187_priv *priv = hw->priv; + + led_classdev_unregister(&led->led_dev); + flush_delayed_work(&priv->led_off); + led->dev = NULL; +} + +void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) +{ + struct rtl8187_priv *priv = dev->priv; + char name[RTL8187_LED_MAX_NAME_LEN + 1]; + u8 ledpin; + int err; + + /* According to the vendor driver, the LED operation depends on the + * customer ID encoded in the EEPROM + */ + printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); + switch (custid) { + case EEPROM_CID_RSVD0: + case EEPROM_CID_RSVD1: + case EEPROM_CID_SERCOMM_PS: + case EEPROM_CID_QMI: + case EEPROM_CID_DELL: + case EEPROM_CID_TOSHIBA: + ledpin = LED_PIN_GPIO0; + break; + case EEPROM_CID_ALPHA0: + ledpin = LED_PIN_LED0; + break; + case EEPROM_CID_HW: + ledpin = LED_PIN_HW; + break; + default: + ledpin = LED_PIN_GPIO0; + } + + INIT_DELAYED_WORK(&priv->led_on, led_turn_on); + INIT_DELAYED_WORK(&priv->led_off, led_turn_off); + + snprintf(name, sizeof(name), + "rtl8187-%s::radio", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_radio, name, + ieee80211_get_radio_led_name(dev), ledpin, true); + if (err) + return; + + snprintf(name, sizeof(name), + "rtl8187-%s::tx", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_tx, name, + ieee80211_get_tx_led_name(dev), ledpin, false); + if (err) + goto err_tx; + + snprintf(name, sizeof(name), + "rtl8187-%s::rx", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_rx, name, + ieee80211_get_rx_led_name(dev), ledpin, false); + if (!err) + return; + + /* registration of RX LED failed - unregister */ + rtl8187_unregister_led(&priv->led_tx); +err_tx: + rtl8187_unregister_led(&priv->led_radio); +} + +void rtl8187_leds_exit(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + + rtl8187_unregister_led(&priv->led_radio); + rtl8187_unregister_led(&priv->led_rx); + rtl8187_unregister_led(&priv->led_tx); + cancel_delayed_work_sync(&priv->led_off); + cancel_delayed_work_sync(&priv->led_on); +} +#endif /* def CONFIG_RTL8187_LEDS */ + diff --git a/drivers/net/wireless/rtl818x/rtl8187/leds.h b/drivers/net/wireless/rtl818x/rtl8187/leds.h new file mode 100644 index 000000000000..d743c96d4a20 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/leds.h @@ -0,0 +1,59 @@ +/* + * Definitions for RTL8187 leds + * + * Copyright 2009 Larry Finger + * + * Based on the LED handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * 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. + */ + +#ifndef RTL8187_LED_H +#define RTL8187_LED_H + +#ifdef CONFIG_RTL8187_LEDS + +#define RTL8187_LED_MAX_NAME_LEN 21 + +#include +#include + +enum { + LED_PIN_LED0, + LED_PIN_LED1, + LED_PIN_GPIO0, + LED_PIN_HW +}; + +enum { + EEPROM_CID_RSVD0 = 0x00, + EEPROM_CID_RSVD1 = 0xFF, + EEPROM_CID_ALPHA0 = 0x01, + EEPROM_CID_SERCOMM_PS = 0x02, + EEPROM_CID_HW = 0x03, + EEPROM_CID_TOSHIBA = 0x04, + EEPROM_CID_QMI = 0x07, + EEPROM_CID_DELL = 0x08 +}; + +struct rtl8187_led { + struct ieee80211_hw *dev; + /* The LED class device */ + struct led_classdev led_dev; + /* The pin/method used to control the led */ + u8 ledpin; + /* The unique name string for this LED device. */ + char name[RTL8187_LED_MAX_NAME_LEN + 1]; + /* If the LED is radio or tx/rx */ + bool is_radio; +}; + +void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); +void rtl8187_leds_exit(struct ieee80211_hw *dev); + +#endif /* def CONFIG_RTL8187_LEDS */ + +#endif /* RTL8187_LED_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c new file mode 100644 index 000000000000..34116719974a --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c @@ -0,0 +1,64 @@ +/* + * Linux RFKILL support for RTL8187 + * + * Copyright (c) 2009 Herton Ronaldo Krzesinski + * + * Based on the RFKILL handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * Thanks to Realtek for their support! + * + * 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 +#include +#include + +#include "rtl8187.h" +#include "rfkill.h" + +static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) +{ + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask); + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & priv->rfkill_mask; +} + +void rtl8187_rfkill_init(struct ieee80211_hw *hw) +{ + struct rtl8187_priv *priv = hw->priv; + + priv->rfkill_off = rtl8187_is_radio_enabled(priv); + printk(KERN_INFO "rtl8187: wireless switch is %s\n", + priv->rfkill_off ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); + wiphy_rfkill_start_polling(hw->wiphy); +} + +void rtl8187_rfkill_poll(struct ieee80211_hw *hw) +{ + bool enabled; + struct rtl8187_priv *priv = hw->priv; + + mutex_lock(&priv->conf_mutex); + enabled = rtl8187_is_radio_enabled(priv); + if (unlikely(enabled != priv->rfkill_off)) { + priv->rfkill_off = enabled; + printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", + enabled ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); + } + mutex_unlock(&priv->conf_mutex); +} + +void rtl8187_rfkill_exit(struct ieee80211_hw *hw) +{ + wiphy_rfkill_stop_polling(hw->wiphy); +} diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.h b/drivers/net/wireless/rtl818x/rtl8187/rfkill.h new file mode 100644 index 000000000000..e12575e96d11 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.h @@ -0,0 +1,8 @@ +#ifndef RTL8187_RFKILL_H +#define RTL8187_RFKILL_H + +void rtl8187_rfkill_init(struct ieee80211_hw *hw); +void rtl8187_rfkill_poll(struct ieee80211_hw *hw); +void rtl8187_rfkill_exit(struct ieee80211_hw *hw); + +#endif /* RTL8187_RFKILL_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h new file mode 100644 index 000000000000..0d7b1423f77b --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h @@ -0,0 +1,271 @@ +/* + * Definitions for RTL8187 hardware + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * 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. + */ + +#ifndef RTL8187_H +#define RTL8187_H + +#include "rtl818x.h" +#include "leds.h" + +#define RTL8187_EEPROM_TXPWR_BASE 0x05 +#define RTL8187_EEPROM_MAC_ADDR 0x07 +#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ +#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ +#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ +#define RTL8187_EEPROM_SELECT_GPIO 0x3B + +#define RTL8187_REQT_READ 0xC0 +#define RTL8187_REQT_WRITE 0x40 +#define RTL8187_REQ_GET_REG 0x05 +#define RTL8187_REQ_SET_REG 0x05 + +#define RTL8187_MAX_RX 0x9C4 + +#define RFKILL_MASK_8187_89_97 0x2 +#define RFKILL_MASK_8198 0x4 + +struct rtl8187_rx_info { + struct urb *urb; + struct ieee80211_hw *dev; +}; + +struct rtl8187_rx_hdr { + __le32 flags; + u8 noise; + u8 signal; + u8 agc; + u8 reserved; + __le64 mac_time; +} __packed; + +struct rtl8187b_rx_hdr { + __le32 flags; + __le64 mac_time; + u8 sq; + u8 rssi; + u8 agc; + u8 flags2; + __le16 snr_long2end; + s8 pwdb_g12; + u8 fot; +} __packed; + +/* {rtl8187,rtl8187b}_tx_info is in skb */ + +struct rtl8187_tx_hdr { + __le32 flags; + __le16 rts_duration; + __le16 len; + __le32 retry; +} __packed; + +struct rtl8187b_tx_hdr { + __le32 flags; + __le16 rts_duration; + __le16 len; + __le32 unused_1; + __le16 unused_2; + __le16 tx_duration; + __le32 unused_3; + __le32 retry; + __le32 unused_4[2]; +} __packed; + +enum { + DEVICE_RTL8187, + DEVICE_RTL8187B +}; + +struct rtl8187_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr *map; + const struct rtl818x_rf_ops *rf; + struct ieee80211_vif *vif; + + /* The mutex protects the TX loopback state. + * Any attempt to set channels concurrently locks the device. + */ + struct mutex conf_mutex; + + /* rtl8187 specific */ + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct usb_device *udev; + u32 rx_conf; + struct usb_anchor anchored; + struct delayed_work work; + struct ieee80211_hw *dev; +#ifdef CONFIG_RTL8187_LEDS + struct rtl8187_led led_radio; + struct rtl8187_led led_tx; + struct rtl8187_led led_rx; + struct delayed_work led_on; + struct delayed_work led_off; +#endif + u16 txpwr_base; + u8 asic_rev; + u8 is_rtl8187b; + enum { + RTL8187BvB, + RTL8187BvD, + RTL8187BvE + } hw_rev; + struct sk_buff_head rx_queue; + u8 signal; + u8 noise; + u8 slot_time; + u8 aifsn[4]; + u8 rfkill_mask; + struct { + __le64 buf; + struct sk_buff_head queue; + } b_tx_status; /* This queue is used by both -b and non-b devices */ + struct mutex io_mutex; + union { + u8 bits8; + __le16 bits16; + __le32 bits32; + } *io_dmabuf; + bool rfkill_off; +}; + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); + +static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv, + u8 *addr, u8 idx) +{ + u8 val; + + mutex_lock(&priv->io_mutex); + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); + + val = priv->io_dmabuf->bits8; + mutex_unlock(&priv->io_mutex); + + return val; +} + +static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr) +{ + return rtl818x_ioread8_idx(priv, addr, 0); +} + +static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv, + __le16 *addr, u8 idx) +{ + __le16 val; + + mutex_lock(&priv->io_mutex); + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); + + val = priv->io_dmabuf->bits16; + mutex_unlock(&priv->io_mutex); + + return le16_to_cpu(val); +} + +static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr) +{ + return rtl818x_ioread16_idx(priv, addr, 0); +} + +static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv, + __le32 *addr, u8 idx) +{ + __le32 val; + + mutex_lock(&priv->io_mutex); + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); + + val = priv->io_dmabuf->bits32; + mutex_unlock(&priv->io_mutex); + + return le32_to_cpu(val); +} + +static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr) +{ + return rtl818x_ioread32_idx(priv, addr, 0); +} + +static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv, + u8 *addr, u8 val, u8 idx) +{ + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits8 = val; + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); + + mutex_unlock(&priv->io_mutex); +} + +static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val) +{ + rtl818x_iowrite8_idx(priv, addr, val, 0); +} + +static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv, + __le16 *addr, u16 val, u8 idx) +{ + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits16 = cpu_to_le16(val); + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); + + mutex_unlock(&priv->io_mutex); +} + +static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr, + u16 val) +{ + rtl818x_iowrite16_idx(priv, addr, val, 0); +} + +static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv, + __le32 *addr, u32 val, u8 idx) +{ + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits32 = cpu_to_le32(val); + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); + + mutex_unlock(&priv->io_mutex); +} + +static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr, + u32 val) +{ + rtl818x_iowrite32_idx(priv, addr, val, 0); +} + +#endif /* RTL8187_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c new file mode 100644 index 000000000000..908903f721f5 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c @@ -0,0 +1,961 @@ +/* + * Radio tuning for RTL8225 on RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * Magic delays, register offsets, and phy value tables below are + * taken from the original r8187 driver sources. Thanks to Realtek + * for their support! + * + * 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 +#include +#include + +#include "rtl8187.h" +#include "rtl8225.h" + +static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg84, reg82; + u32 bangdata; + int i; + + bangdata = (data << 4) | (addr & 0xf); + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); + + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(10); + + for (i = 15; i >= 0; i--) { + u16 reg = reg80 | (bangdata & (1 << i)) >> i; + + if (i & 1) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + + if (!(i & 1)) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); +} + +static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg82, reg84; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + + reg80 &= ~(0x3 << 2); + reg84 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(10); + + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits16 = data; + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + addr, 0x8225, &priv->io_dmabuf->bits16, sizeof(data), + HZ / 2); + + mutex_unlock(&priv->io_mutex); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); +} + +static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + + if (priv->asic_rev) + rtl8225_write_8051(dev, addr, cpu_to_le16(data)); + else + rtl8225_write_bitbang(dev, addr, data); +} + +static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg82, reg84, out; + int i; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + + reg80 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(4); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(5); + + for (i = 4; i >= 0; i--) { + u16 reg = reg80 | ((addr >> i) & 1); + + if (!(i & 1)) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + udelay(1); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + udelay(2); + + if (i & 1) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + udelay(1); + } + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + + out = 0; + for (i = 11; i >= 0; i--) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(1); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + + if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) + out |= 1 << i; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 2)); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); + + return out; +} + +static const u16 rtl8225bcd_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static const u8 rtl8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, + 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, + 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, + 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, + 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, + 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, + 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static const u8 rtl8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dBm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dBm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dBm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dBm */ +}; + +static const u8 rtl8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd +}; + +static const u8 rtl8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static const u8 rtl8225_tx_power_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static const u8 rtl8225_tx_power_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225_tx_power_ofdm[] = { + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static const u32 rtl8225_chan[] = { + 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, + 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 +}; + +static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xF; + ofdm_power = priv->channels[channel - 1].hw_value >> 4; + + cck_power = min(cck_power, (u8)11); + if (ofdm_power > (u8)15) + ofdm_power = 25; + else + ofdm_power += 10; + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); + + if (channel == 14) + tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; + else + tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + msleep(1); // FIXME: optional? + + /* anaparam2 on */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, + RTL8187_RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write_phy_ofdm(dev, 2, 0x42); + rtl8225_write_phy_ofdm(dev, 6, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x00); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1); + + tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; + + rtl8225_write_phy_ofdm(dev, 5, *tmp); + rtl8225_write_phy_ofdm(dev, 7, *tmp); + + msleep(1); +} + +static void rtl8225_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x067); + rtl8225_write(dev, 0x1, 0xFE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x486); + rtl8225_write(dev, 0x5, 0xBC0); + rtl8225_write(dev, 0x6, 0xAE6); + rtl8225_write(dev, 0x7, 0x82A); + rtl8225_write(dev, 0x8, 0x01F); + rtl8225_write(dev, 0x9, 0x334); + rtl8225_write(dev, 0xA, 0xFD4); + rtl8225_write(dev, 0xB, 0x391); + rtl8225_write(dev, 0xC, 0x050); + rtl8225_write(dev, 0xD, 0x6DB); + rtl8225_write(dev, 0xE, 0x029); + rtl8225_write(dev, 0xF, 0x914); msleep(100); + + rtl8225_write(dev, 0x2, 0xC4D); msleep(200); + rtl8225_write(dev, 0x2, 0x44D); msleep(200); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0c4d); + msleep(200); + rtl8225_write(dev, 0x02, 0x044d); + msleep(100); + if (!(rtl8225_read(dev, 6) & (1 << 7))) + wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", + rtl8225_read(dev, 6)); + } + + rtl8225_write(dev, 0x0, 0x127); + + for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x027); + rtl8225_write(dev, 0x0, 0x22F); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); + rtl8225_write_phy_ofdm(dev, 0x02, 0x42); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); + rtl8225_write_phy_ofdm(dev, 0x11, 0x06); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); + + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); + rtl8225_write_phy_cck(dev, 0x03, 0x20); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); + rtl8225_write_phy_cck(dev, 0x05, 0x12); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); + rtl8225_write_phy_cck(dev, 0x07, 0x78); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); + rtl8225_write_phy_cck(dev, 0x10, 0x9b); + rtl8225_write_phy_cck(dev, 0x11, 0x88); + rtl8225_write_phy_cck(dev, 0x12, 0x47); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); + rtl8225_write_phy_cck(dev, 0x42, 0x15); + rtl8225_write_phy_cck(dev, 0x43, 0x18); + rtl8225_write_phy_cck(dev, 0x44, 0x1f); + rtl8225_write_phy_cck(dev, 0x45, 0x1e); + rtl8225_write_phy_cck(dev, 0x46, 0x1a); + rtl8225_write_phy_cck(dev, 0x47, 0x15); + rtl8225_write_phy_cck(dev, 0x48, 0x10); + rtl8225_write_phy_cck(dev, 0x49, 0x0a); + rtl8225_write_phy_cck(dev, 0x4a, 0x05); + rtl8225_write_phy_cck(dev, 0x4b, 0x02); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); + + rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); + + rtl8225_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); + + /* set sensitivity */ + rtl8225_write(dev, 0x0c, 0x50); + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); + rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]); +} + +static const u8 rtl8225z2_agc[] = { + 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, + 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, + 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, + 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, + 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, + 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 +}; +static const u8 rtl8225z2_ofdm[] = { + 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, + 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, + 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f, + 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, + 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, + 0x6d, 0x3c, 0xfb, 0x07 +}; + +static const u8 rtl8225z2_tx_power_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225z2_tx_power_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 +}; + +static const u8 rtl8225z2_tx_power_ofdm[] = { + 0x42, 0x00, 0x40, 0x00, 0x40 +}; + +static const u8 rtl8225z2_tx_gain_cck_ofdm[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 +}; + +static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xF; + ofdm_power = priv->channels[channel - 1].hw_value >> 4; + + cck_power = min(cck_power, (u8)15); + cck_power += priv->txpwr_base & 0xF; + cck_power = min(cck_power, (u8)35); + + if (ofdm_power > (u8)15) + ofdm_power = 25; + else + ofdm_power += 10; + ofdm_power += priv->txpwr_base >> 4; + ofdm_power = min(ofdm_power, (u8)35); + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else + tmp = rtl8225z2_tx_power_cck; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225z2_tx_gain_cck_ofdm[cck_power]); + msleep(1); + + /* anaparam2 on */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, + RTL8187_RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write_phy_ofdm(dev, 2, 0x42); + rtl8225_write_phy_ofdm(dev, 5, 0x00); + rtl8225_write_phy_ofdm(dev, 6, 0x40); + rtl8225_write_phy_ofdm(dev, 7, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x40); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225z2_tx_gain_cck_ofdm[ofdm_power]); + msleep(1); +} + +static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xF; + ofdm_power = priv->channels[channel - 1].hw_value >> 4; + + if (cck_power > 15) + cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22; + else + cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7; + cck_power += priv->txpwr_base & 0xF; + cck_power = min(cck_power, (u8)35); + + if (ofdm_power > 15) + ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25; + else + ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10; + ofdm_power += (priv->txpwr_base >> 4) & 0xF; + ofdm_power = min(ofdm_power, (u8)35); + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else + tmp = rtl8225z2_tx_power_cck; + + if (priv->hw_rev == RTL8187BvB) { + if (cck_power <= 6) + ; /* do nothing */ + else if (cck_power <= 11) + tmp += 8; + else + tmp += 16; + } else { + if (cck_power <= 5) + ; /* do nothing */ + else if (cck_power <= 11) + tmp += 8; + else if (cck_power <= 17) + tmp += 16; + else + tmp += 24; + } + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225z2_tx_gain_cck_ofdm[cck_power] << 1); + msleep(1); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1); + if (priv->hw_rev == RTL8187BvB) { + if (ofdm_power <= 11) { + rtl8225_write_phy_ofdm(dev, 0x87, 0x60); + rtl8225_write_phy_ofdm(dev, 0x89, 0x60); + } else { + rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); + rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); + } + } else { + if (ofdm_power <= 11) { + rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); + rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); + } else if (ofdm_power <= 17) { + rtl8225_write_phy_ofdm(dev, 0x87, 0x54); + rtl8225_write_phy_ofdm(dev, 0x89, 0x54); + } else { + rtl8225_write_phy_ofdm(dev, 0x87, 0x50); + rtl8225_write_phy_ofdm(dev, 0x89, 0x50); + } + } + msleep(1); +} + +static const u16 rtl8225z2_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static const u8 rtl8225z2_gain_bg[] = { + 0x23, 0x15, 0xa5, /* -82-1dBm */ + 0x23, 0x15, 0xb5, /* -82-2dBm */ + 0x23, 0x15, 0xc5, /* -82-3dBm */ + 0x33, 0x15, 0xc5, /* -78dBm */ + 0x43, 0x15, 0xc5, /* -74dBm */ + 0x53, 0x15, 0xc5, /* -70dBm */ + 0x63, 0x15, 0xc5 /* -66dBm */ +}; + +static void rtl8225z2_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x2BF); + rtl8225_write(dev, 0x1, 0xEE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x8C3); + rtl8225_write(dev, 0x5, 0xC72); + rtl8225_write(dev, 0x6, 0x0E6); + rtl8225_write(dev, 0x7, 0x82A); + rtl8225_write(dev, 0x8, 0x03F); + rtl8225_write(dev, 0x9, 0x335); + rtl8225_write(dev, 0xa, 0x9D4); + rtl8225_write(dev, 0xb, 0x7BB); + rtl8225_write(dev, 0xc, 0x850); + rtl8225_write(dev, 0xd, 0xCDF); + rtl8225_write(dev, 0xe, 0x02B); + rtl8225_write(dev, 0xf, 0x114); + msleep(100); + + rtl8225_write(dev, 0x0, 0x1B7); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x3, 0x080); + rtl8225_write(dev, 0x5, 0x004); + rtl8225_write(dev, 0x0, 0x0B7); + rtl8225_write(dev, 0x2, 0xc4D); + + msleep(200); + rtl8225_write(dev, 0x2, 0x44D); + msleep(100); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0C4D); + msleep(200); + rtl8225_write(dev, 0x02, 0x044D); + msleep(100); + if (!(rtl8225_read(dev, 6) & (1 << 7))) + wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", + rtl8225_read(dev, 6)); + } + + msleep(200); + + rtl8225_write(dev, 0x0, 0x2BF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); + rtl8225_write_phy_ofdm(dev, 0x02, 0x42); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); + rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); + rtl8225_write_phy_ofdm(dev, 0x11, 0x07); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); + rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); + rtl8225_write_phy_ofdm(dev, 0x21, 0x17); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); + rtl8225_write_phy_ofdm(dev, 0x23, 0x80); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); + rtl8225_write_phy_ofdm(dev, 0x25, 0x00); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); + + rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]); + rtl8225_write_phy_ofdm(dev, 0x21, 0x37); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); + rtl8225_write_phy_cck(dev, 0x03, 0x20); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); + rtl8225_write_phy_cck(dev, 0x05, 0x12); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); + rtl8225_write_phy_cck(dev, 0x07, 0x78); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); + rtl8225_write_phy_cck(dev, 0x10, 0x9b); + rtl8225_write_phy_cck(dev, 0x11, 0x88); + rtl8225_write_phy_cck(dev, 0x12, 0x47); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); + rtl8225_write_phy_cck(dev, 0x42, 0x15); + rtl8225_write_phy_cck(dev, 0x43, 0x18); + rtl8225_write_phy_cck(dev, 0x44, 0x36); + rtl8225_write_phy_cck(dev, 0x45, 0x35); + rtl8225_write_phy_cck(dev, 0x46, 0x2e); + rtl8225_write_phy_cck(dev, 0x47, 0x25); + rtl8225_write_phy_cck(dev, 0x48, 0x1c); + rtl8225_write_phy_cck(dev, 0x49, 0x12); + rtl8225_write_phy_cck(dev, 0x4a, 0x09); + rtl8225_write_phy_cck(dev, 0x4b, 0x04); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); + + rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1); + + rtl8225z2_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); +} + +static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x0B7); + rtl8225_write(dev, 0x1, 0xEE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x8C3); + rtl8225_write(dev, 0x5, 0xC72); + rtl8225_write(dev, 0x6, 0x0E6); + rtl8225_write(dev, 0x7, 0x82A); + rtl8225_write(dev, 0x8, 0x03F); + rtl8225_write(dev, 0x9, 0x335); + rtl8225_write(dev, 0xa, 0x9D4); + rtl8225_write(dev, 0xb, 0x7BB); + rtl8225_write(dev, 0xc, 0x850); + rtl8225_write(dev, 0xd, 0xCDF); + rtl8225_write(dev, 0xe, 0x02B); + rtl8225_write(dev, 0xf, 0x114); + + rtl8225_write(dev, 0x0, 0x1B7); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x3, 0x080); + rtl8225_write(dev, 0x5, 0x004); + rtl8225_write(dev, 0x0, 0x0B7); + + rtl8225_write(dev, 0x2, 0xC4D); + + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x0, 0x2BF); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03); + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07); + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); + + rtl8225_write_phy_ofdm(dev, 0x80, 0x12); + for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]); + rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i); + rtl8225_write_phy_ofdm(dev, 0xE, 0); + } + rtl8225_write_phy_ofdm(dev, 0x80, 0x10); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++) + rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]); + + rtl8225_write_phy_ofdm(dev, 0x97, 0x46); + rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); + rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); + rtl8225_write_phy_cck(dev, 0xc1, 0x88); +} + +static void rtl8225_rf_stop(struct ieee80211_hw *dev) +{ + rtl8225_write(dev, 0x4, 0x1f); +} + +static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8187_priv *priv = dev->priv; + int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); + + if (priv->rf->init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, chan); + else if (priv->rf->init == rtl8225z2_rf_init) + rtl8225z2_rf_set_tx_power(dev, chan); + else + rtl8225z2_b_rf_set_tx_power(dev, chan); + + rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); + msleep(10); +} + +static const struct rtl818x_rf_ops rtl8225_ops = { + .name = "rtl8225", + .init = rtl8225_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +static const struct rtl818x_rf_ops rtl8225z2_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +static const struct rtl818x_rf_ops rtl8225z2_b_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_b_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev) +{ + u16 reg8, reg9; + struct rtl8187_priv *priv = dev->priv; + + if (!priv->is_rtl8187b) { + rtl8225_write(dev, 0, 0x1B7); + + reg8 = rtl8225_read(dev, 8); + reg9 = rtl8225_read(dev, 9); + + rtl8225_write(dev, 0, 0x0B7); + + if (reg8 != 0x588 || reg9 != 0x700) + return &rtl8225_ops; + + return &rtl8225z2_ops; + } else + return &rtl8225z2_b_ops; +} diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h new file mode 100644 index 000000000000..20c5b6ead0f6 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h @@ -0,0 +1,44 @@ +/* + * Radio tuning definitions for RTL8225 on RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * 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. + */ + +#ifndef RTL8187_RTL8225_H +#define RTL8187_RTL8225_H + +#define RTL8187_RTL8225_ANAPARAM_ON 0xa0000a59 +#define RTL8187_RTL8225_ANAPARAM2_ON 0x860c7312 +#define RTL8187_RTL8225_ANAPARAM_OFF 0xa00beb59 +#define RTL8187_RTL8225_ANAPARAM2_OFF 0x840dec11 + +#define RTL8187B_RTL8225_ANAPARAM_ON 0x45090658 +#define RTL8187B_RTL8225_ANAPARAM2_ON 0x727f3f52 +#define RTL8187B_RTL8225_ANAPARAM3_ON 0x00 +#define RTL8187B_RTL8225_ANAPARAM_OFF 0x55480658 +#define RTL8187B_RTL8225_ANAPARAM2_OFF 0x72003f50 +#define RTL8187B_RTL8225_ANAPARAM3_OFF 0x00 + +const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *); + +static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, + u8 addr, u32 data) +{ + rtl8187_write_phy(dev, addr, data); +} + +static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, + u8 addr, u32 data) +{ + rtl8187_write_phy(dev, addr, data | 0x10000); +} + +#endif /* RTL8187_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c deleted file mode 100644 index eeee244fcaab..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * Linux device driver for RTL8187 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * The driver was extended to the RTL8187B in 2008 by: - * Herton Ronaldo Krzesinski - * Hin-Tak Leung - * Larry Finger - * - * Magic delays and register offsets below are taken from the original - * r8187 driver sources. Thanks to Realtek for their support! - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "rtl8187.h" -#include "rtl8187_rtl8225.h" -#ifdef CONFIG_RTL8187_LEDS -#include "rtl8187_leds.h" -#endif -#include "rtl8187_rfkill.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_AUTHOR("Andrea Merello "); -MODULE_AUTHOR("Herton Ronaldo Krzesinski "); -MODULE_AUTHOR("Hin-Tak Leung "); -MODULE_AUTHOR("Larry Finger "); -MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); -MODULE_LICENSE("GPL"); - -static struct usb_device_id rtl8187_table[] __devinitdata = { - /* Asus */ - {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, - /* Belkin */ - {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B}, - /* Realtek */ - {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, - {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B}, - {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B}, - /* Surecom */ - {USB_DEVICE(0x0769, 0x11F2), .driver_info = DEVICE_RTL8187}, - /* Logitech */ - {USB_DEVICE(0x0789, 0x010C), .driver_info = DEVICE_RTL8187}, - /* Netgear */ - {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, - /* HP */ - {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, - /* Sitecom */ - {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, - {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B}, - /* Sphairon Access Systems GmbH */ - {USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187}, - /* Dick Smith Electronics */ - {USB_DEVICE(0x1371, 0x9401), .driver_info = DEVICE_RTL8187}, - /* Abocom */ - {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, - /* Qcom */ - {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, - /* AirLive */ - {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, - /* Linksys */ - {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, - {} -}; - -MODULE_DEVICE_TABLE(usb, rtl8187_table); - -static const struct ieee80211_rate rtl818x_rates[] = { - { .bitrate = 10, .hw_value = 0, }, - { .bitrate = 20, .hw_value = 1, }, - { .bitrate = 55, .hw_value = 2, }, - { .bitrate = 110, .hw_value = 3, }, - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static const struct ieee80211_channel rtl818x_channels[] = { - { .center_freq = 2412 }, - { .center_freq = 2417 }, - { .center_freq = 2422 }, - { .center_freq = 2427 }, - { .center_freq = 2432 }, - { .center_freq = 2437 }, - { .center_freq = 2442 }, - { .center_freq = 2447 }, - { .center_freq = 2452 }, - { .center_freq = 2457 }, - { .center_freq = 2462 }, - { .center_freq = 2467 }, - { .center_freq = 2472 }, - { .center_freq = 2484 }, -}; - -static void rtl8187_iowrite_async_cb(struct urb *urb) -{ - kfree(urb->context); -} - -static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, - void *data, u16 len) -{ - struct usb_ctrlrequest *dr; - struct urb *urb; - struct rtl8187_async_write_data { - u8 data[4]; - struct usb_ctrlrequest dr; - } *buf; - int rc; - - buf = kmalloc(sizeof(*buf), GFP_ATOMIC); - if (!buf) - return; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - kfree(buf); - return; - } - - dr = &buf->dr; - - dr->bRequestType = RTL8187_REQT_WRITE; - dr->bRequest = RTL8187_REQ_SET_REG; - dr->wValue = addr; - dr->wIndex = 0; - dr->wLength = cpu_to_le16(len); - - memcpy(buf, data, len); - - usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), - (unsigned char *)dr, buf, len, - rtl8187_iowrite_async_cb, buf); - usb_anchor_urb(urb, &priv->anchored); - rc = usb_submit_urb(urb, GFP_ATOMIC); - if (rc < 0) { - kfree(buf); - usb_unanchor_urb(urb); - } - usb_free_urb(urb); -} - -static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, - __le32 *addr, u32 val) -{ - __le32 buf = cpu_to_le32(val); - - rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr), - &buf, sizeof(buf)); -} - -void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8187_priv *priv = dev->priv; - - data <<= 8; - data |= addr | 0x80; - - rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); - rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); - rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); - rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); -} - -static void rtl8187_tx_cb(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *)urb->context; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hw *hw = info->rate_driver_data[0]; - struct rtl8187_priv *priv = hw->priv; - - skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : - sizeof(struct rtl8187_tx_hdr)); - ieee80211_tx_info_clear_status(info); - - if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - if (priv->is_rtl8187b) { - skb_queue_tail(&priv->b_tx_status.queue, skb); - - /* queue is "full", discard last items */ - while (skb_queue_len(&priv->b_tx_status.queue) > 5) { - struct sk_buff *old_skb; - - dev_dbg(&priv->udev->dev, - "transmit status queue full\n"); - - old_skb = skb_dequeue(&priv->b_tx_status.queue); - ieee80211_tx_status_irqsafe(hw, old_skb); - } - return; - } else { - info->flags |= IEEE80211_TX_STAT_ACK; - } - } - if (priv->is_rtl8187b) - ieee80211_tx_status_irqsafe(hw, skb); - else { - /* Retry information for the RTI8187 is only available by - * reading a register in the device. We are in interrupt mode - * here, thus queue the skb and finish on a work queue. */ - skb_queue_tail(&priv->b_tx_status.queue, skb); - ieee80211_queue_delayed_work(hw, &priv->work, 0); - } -} - -static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct rtl8187_priv *priv = dev->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - unsigned int ep; - void *buf; - struct urb *urb; - __le16 rts_dur = 0; - u32 flags; - int rc; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - flags = skb->len; - flags |= RTL818X_TX_DESC_FLAG_NO_ENC; - - flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; - if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) - flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { - flags |= RTL818X_TX_DESC_FLAG_RTS; - flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - rts_dur = ieee80211_rts_duration(dev, priv->vif, - skb->len, info); - } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - flags |= RTL818X_TX_DESC_FLAG_CTS; - flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - } - - if (!priv->is_rtl8187b) { - struct rtl8187_tx_hdr *hdr = - (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr)); - hdr->flags = cpu_to_le32(flags); - hdr->len = 0; - hdr->rts_duration = rts_dur; - hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); - buf = hdr; - - ep = 2; - } else { - /* fc needs to be calculated before skb_push() */ - unsigned int epmap[4] = { 6, 7, 5, 4 }; - struct ieee80211_hdr *tx_hdr = - (struct ieee80211_hdr *)(skb->data); - u16 fc = le16_to_cpu(tx_hdr->frame_control); - - struct rtl8187b_tx_hdr *hdr = - (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr)); - struct ieee80211_rate *txrate = - ieee80211_get_tx_rate(dev, info); - memset(hdr, 0, sizeof(*hdr)); - hdr->flags = cpu_to_le32(flags); - hdr->rts_duration = rts_dur; - hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); - hdr->tx_duration = - ieee80211_generic_frame_duration(dev, priv->vif, - skb->len, txrate); - buf = hdr; - - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) - ep = 12; - else - ep = epmap[skb_get_queue_mapping(skb)]; - } - - info->rate_driver_data[0] = dev; - info->rate_driver_data[1] = urb; - - usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), - buf, skb->len, rtl8187_tx_cb, skb); - urb->transfer_flags |= URB_ZERO_PACKET; - usb_anchor_urb(urb, &priv->anchored); - rc = usb_submit_urb(urb, GFP_ATOMIC); - if (rc < 0) { - usb_unanchor_urb(urb); - kfree_skb(skb); - } - usb_free_urb(urb); - - return NETDEV_TX_OK; -} - -static void rtl8187_rx_cb(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *)urb->context; - struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; - struct ieee80211_hw *dev = info->dev; - struct rtl8187_priv *priv = dev->priv; - struct ieee80211_rx_status rx_status = { 0 }; - int rate, signal; - u32 flags; - unsigned long f; - - spin_lock_irqsave(&priv->rx_queue.lock, f); - __skb_unlink(skb, &priv->rx_queue); - spin_unlock_irqrestore(&priv->rx_queue.lock, f); - skb_put(skb, urb->actual_length); - - if (unlikely(urb->status)) { - dev_kfree_skb_irq(skb); - return; - } - - if (!priv->is_rtl8187b) { - struct rtl8187_rx_hdr *hdr = - (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - flags = le32_to_cpu(hdr->flags); - /* As with the RTL8187B below, the AGC is used to calculate - * signal strength. In this case, the scaling - * constants are derived from the output of p54usb. - */ - signal = -4 - ((27 * hdr->agc) >> 6); - rx_status.antenna = (hdr->signal >> 7) & 1; - rx_status.mactime = le64_to_cpu(hdr->mac_time); - } else { - struct rtl8187b_rx_hdr *hdr = - (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - /* The Realtek datasheet for the RTL8187B shows that the RX - * header contains the following quantities: signal quality, - * RSSI, AGC, the received power in dB, and the measured SNR. - * In testing, none of these quantities show qualitative - * agreement with AP signal strength, except for the AGC, - * which is inversely proportional to the strength of the - * signal. In the following, the signal strength - * is derived from the AGC. The arbitrary scaling constants - * are chosen to make the results close to the values obtained - * for a BCM4312 using b43 as the driver. The noise is ignored - * for now. - */ - flags = le32_to_cpu(hdr->flags); - signal = 14 - hdr->agc / 2; - rx_status.antenna = (hdr->rssi >> 7) & 1; - rx_status.mactime = le64_to_cpu(hdr->mac_time); - } - - rx_status.signal = signal; - priv->signal = signal; - rate = (flags >> 20) & 0xF; - skb_trim(skb, flags & 0x0FFF); - rx_status.rate_idx = rate; - rx_status.freq = dev->conf.channel->center_freq; - rx_status.band = dev->conf.channel->band; - rx_status.flag |= RX_FLAG_TSFT; - if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) - rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(dev, skb); - - skb = dev_alloc_skb(RTL8187_MAX_RX); - if (unlikely(!skb)) { - /* TODO check rx queue length and refill *somewhere* */ - return; - } - - info = (struct rtl8187_rx_info *)skb->cb; - info->urb = urb; - info->dev = dev; - urb->transfer_buffer = skb_tail_pointer(skb); - urb->context = skb; - skb_queue_tail(&priv->rx_queue, skb); - - usb_anchor_urb(urb, &priv->anchored); - if (usb_submit_urb(urb, GFP_ATOMIC)) { - usb_unanchor_urb(urb); - skb_unlink(skb, &priv->rx_queue); - dev_kfree_skb_irq(skb); - } -} - -static int rtl8187_init_urbs(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - struct urb *entry = NULL; - struct sk_buff *skb; - struct rtl8187_rx_info *info; - int ret = 0; - - while (skb_queue_len(&priv->rx_queue) < 16) { - skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); - if (!skb) { - ret = -ENOMEM; - goto err; - } - entry = usb_alloc_urb(0, GFP_KERNEL); - if (!entry) { - ret = -ENOMEM; - goto err; - } - usb_fill_bulk_urb(entry, priv->udev, - usb_rcvbulkpipe(priv->udev, - priv->is_rtl8187b ? 3 : 1), - skb_tail_pointer(skb), - RTL8187_MAX_RX, rtl8187_rx_cb, skb); - info = (struct rtl8187_rx_info *)skb->cb; - info->urb = entry; - info->dev = dev; - skb_queue_tail(&priv->rx_queue, skb); - usb_anchor_urb(entry, &priv->anchored); - ret = usb_submit_urb(entry, GFP_KERNEL); - if (ret) { - skb_unlink(skb, &priv->rx_queue); - usb_unanchor_urb(entry); - goto err; - } - usb_free_urb(entry); - } - return ret; - -err: - usb_free_urb(entry); - kfree_skb(skb); - usb_kill_anchored_urbs(&priv->anchored); - return ret; -} - -static void rtl8187b_status_cb(struct urb *urb) -{ - struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; - struct rtl8187_priv *priv = hw->priv; - u64 val; - unsigned int cmd_type; - - if (unlikely(urb->status)) - return; - - /* - * Read from status buffer: - * - * bits [30:31] = cmd type: - * - 0 indicates tx beacon interrupt - * - 1 indicates tx close descriptor - * - * In the case of tx beacon interrupt: - * [0:9] = Last Beacon CW - * [10:29] = reserved - * [30:31] = 00b - * [32:63] = Last Beacon TSF - * - * If it's tx close descriptor: - * [0:7] = Packet Retry Count - * [8:14] = RTS Retry Count - * [15] = TOK - * [16:27] = Sequence No - * [28] = LS - * [29] = FS - * [30:31] = 01b - * [32:47] = unused (reserved?) - * [48:63] = MAC Used Time - */ - val = le64_to_cpu(priv->b_tx_status.buf); - - cmd_type = (val >> 30) & 0x3; - if (cmd_type == 1) { - unsigned int pkt_rc, seq_no; - bool tok; - struct sk_buff *skb; - struct ieee80211_hdr *ieee80211hdr; - unsigned long flags; - - pkt_rc = val & 0xFF; - tok = val & (1 << 15); - seq_no = (val >> 16) & 0xFFF; - - spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); - skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { - ieee80211hdr = (struct ieee80211_hdr *)skb->data; - - /* - * While testing, it was discovered that the seq_no - * doesn't actually contains the sequence number. - * Instead of returning just the 12 bits of sequence - * number, hardware is returning entire sequence control - * (fragment number plus sequence number) in a 12 bit - * only field overflowing after some time. As a - * workaround, just consider the lower bits, and expect - * it's unlikely we wrongly ack some sent data - */ - if ((le16_to_cpu(ieee80211hdr->seq_ctrl) - & 0xFFF) == seq_no) - break; - } - if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - __skb_unlink(skb, &priv->b_tx_status.queue); - if (tok) - info->flags |= IEEE80211_TX_STAT_ACK; - info->status.rates[0].count = pkt_rc + 1; - - ieee80211_tx_status_irqsafe(hw, skb); - } - spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); - } - - usb_anchor_urb(urb, &priv->anchored); - if (usb_submit_urb(urb, GFP_ATOMIC)) - usb_unanchor_urb(urb); -} - -static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - struct urb *entry; - int ret = 0; - - entry = usb_alloc_urb(0, GFP_KERNEL); - if (!entry) - return -ENOMEM; - - usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), - &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), - rtl8187b_status_cb, dev); - - usb_anchor_urb(entry, &priv->anchored); - ret = usb_submit_urb(entry, GFP_KERNEL); - if (ret) - usb_unanchor_urb(entry); - usb_free_urb(entry); - - return ret; -} - -static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) -{ - u32 anaparam, anaparam2; - u8 anaparam3, reg; - - if (!priv->is_rtl8187b) { - if (rfon) { - anaparam = RTL8187_RTL8225_ANAPARAM_ON; - anaparam2 = RTL8187_RTL8225_ANAPARAM2_ON; - } else { - anaparam = RTL8187_RTL8225_ANAPARAM_OFF; - anaparam2 = RTL8187_RTL8225_ANAPARAM2_OFF; - } - } else { - if (rfon) { - anaparam = RTL8187B_RTL8225_ANAPARAM_ON; - anaparam2 = RTL8187B_RTL8225_ANAPARAM2_ON; - anaparam3 = RTL8187B_RTL8225_ANAPARAM3_ON; - } else { - anaparam = RTL8187B_RTL8225_ANAPARAM_OFF; - anaparam2 = RTL8187B_RTL8225_ANAPARAM2_OFF; - anaparam3 = RTL8187B_RTL8225_ANAPARAM3_OFF; - } - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - reg |= RTL818X_CONFIG3_ANAPARAM_WRITE; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); - if (priv->is_rtl8187b) - rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3); - reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_NORMAL); -} - -static int rtl8187_cmd_reset(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - u8 reg; - int i; - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= (1 << 1); - reg |= RTL818X_CMD_RESET; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - i = 10; - do { - msleep(2); - if (!(rtl818x_ioread8(priv, &priv->map->CMD) & - RTL818X_CMD_RESET)) - break; - } while (--i); - - if (!i) { - wiphy_err(dev->wiphy, "Reset timeout!\n"); - return -ETIMEDOUT; - } - - /* reload registers from eeprom */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); - - i = 10; - do { - msleep(4); - if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) & - RTL818X_EEPROM_CMD_CONFIG)) - break; - } while (--i); - - if (!i) { - wiphy_err(dev->wiphy, "eeprom reset timeout!\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int rtl8187_init_hw(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - u8 reg; - int res; - - /* reset */ - rtl8187_set_anaparam(priv, true); - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - - msleep(200); - rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); - rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); - rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); - msleep(200); - - res = rtl8187_cmd_reset(dev); - if (res) - return res; - - rtl8187_set_anaparam(priv, true); - - /* setup card */ - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); - - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - - rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); - reg &= 0x3F; - reg |= 0x80; - rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); - rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); - - // TODO: set RESP_RATE and BRSR properly - rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - - /* host_usb_init */ - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); - reg = rtl818x_ioread8(priv, (u8 *)0xFE53); - rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); - msleep(100); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); - rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_NORMAL); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); - msleep(100); - - priv->rf->init(dev); - - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); - rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); - rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); - rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - - return 0; -} - -static const u8 rtl8187b_reg_table[][3] = { - {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0}, - {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0}, - {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0}, - {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0}, - - {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1}, - {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1}, - {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, - {0xF2, 0x02, 1}, {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, - {0xF6, 0x06, 1}, {0xF7, 0x07, 1}, {0xF8, 0x08, 1}, - - {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2}, - {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2}, - {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2}, - {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2}, - {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2}, - {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2}, - {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, - - {0x5B, 0x40, 0}, {0x84, 0x88, 0}, {0x85, 0x24, 0}, {0x88, 0x54, 0}, - {0x8B, 0xB8, 0}, {0x8C, 0x07, 0}, {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, - {0x95, 0x12, 0}, {0x96, 0x00, 0}, {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, - {0x9F, 0x10, 0}, {0xB4, 0x22, 0}, {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, - {0xEE, 0x00, 0}, {0x4C, 0x00, 2}, - - {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0}, {0x8E, 0x08, 0}, - {0x8F, 0x00, 0} -}; - -static int rtl8187b_init_hw(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int res, i; - u8 reg; - - rtl8187_set_anaparam(priv, true); - - /* Reset PLL sequence on 8187B. Realtek note: reduces power - * consumption about 30 mA */ - rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10); - reg = rtl818x_ioread8(priv, (u8 *)0xFF62); - rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5)); - rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5)); - - res = rtl8187_cmd_reset(dev); - if (res) - return res; - - rtl8187_set_anaparam(priv, true); - - /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as - * RESP_RATE on 8187L in Realtek sources: each bit should be each - * one of the 12 rates, all are enabled */ - rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); - - reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; - rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); - - /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ - rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); - rtl818x_iowrite8_idx(priv, (u8 *)0xFFE2, 0x00, 1); - - rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); - rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { - rtl818x_iowrite8_idx(priv, - (u8 *)(uintptr_t) - (rtl8187b_reg_table[i][0] | 0xFF00), - rtl8187b_reg_table[i][1], - rtl8187b_reg_table[i][2]); - } - - rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); - rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); - - rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1); - rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1); - rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001); - - /* RFSW_CTRL register */ - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - msleep(100); - - priv->rf->init(dev); - - reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); - - rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4); - rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); - rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); - - reg = rtl818x_ioread8(priv, (u8 *)0xFFDB); - rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2)); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3); - rtl818x_iowrite8(priv, (u8 *)0xFF61, 0); - rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1); - rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1); - rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10); - rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2); - - rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B); - - rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); - - priv->slot_time = 0x9; - priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ - priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ - priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ - priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ - rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); - - /* ENEDCA flag must always be set, transmit issues? */ - rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); - - return 0; -} - -static void rtl8187_work(struct work_struct *work) -{ - /* The RTL8187 returns the retry count through register 0xFFFA. In - * addition, it appears to be a cumulative retry count, not the - * value for the current TX packet. When multiple TX entries are - * queued, the retry count will be valid for the last one in the queue. - * The "error" should not matter for purposes of rate setting. */ - struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, - work.work); - struct ieee80211_tx_info *info; - struct ieee80211_hw *dev = priv->dev; - static u16 retry; - u16 tmp; - - mutex_lock(&priv->conf_mutex); - tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); - while (skb_queue_len(&priv->b_tx_status.queue) > 0) { - struct sk_buff *old_skb; - - old_skb = skb_dequeue(&priv->b_tx_status.queue); - info = IEEE80211_SKB_CB(old_skb); - info->status.rates[0].count = tmp - retry + 1; - ieee80211_tx_status_irqsafe(dev, old_skb); - } - retry = tmp; - mutex_unlock(&priv->conf_mutex); -} - -static int rtl8187_start(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - u32 reg; - int ret; - - mutex_lock(&priv->conf_mutex); - - ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : - rtl8187b_init_hw(dev); - if (ret) - goto rtl8187_start_exit; - - init_usb_anchor(&priv->anchored); - priv->dev = dev; - - if (priv->is_rtl8187b) { - reg = RTL818X_RX_CONF_MGMT | - RTL818X_RX_CONF_DATA | - RTL818X_RX_CONF_BROADCAST | - RTL818X_RX_CONF_NICMAC | - RTL818X_RX_CONF_BSSID | - (7 << 13 /* RX FIFO threshold NONE */) | - (7 << 10 /* MAX RX DMA */) | - RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_MULTICAST; - priv->rx_conf = reg; - rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; - rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); - - rtl818x_iowrite32(priv, &priv->map->TX_CONF, - RTL818X_TX_CONF_HW_SEQNUM | - RTL818X_TX_CONF_DISREQQSIZE | - (7 << 8 /* short retry limit */) | - (7 << 0 /* long retry limit */) | - (7 << 21 /* MAX TX DMA */)); - rtl8187_init_urbs(dev); - rtl8187b_init_status_urb(dev); - goto rtl8187_start_exit; - } - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); - - rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); - rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); - - rtl8187_init_urbs(dev); - - reg = RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_BSSID | - RTL818X_RX_CONF_MGMT | - RTL818X_RX_CONF_DATA | - (7 << 13 /* RX FIFO threshold NONE */) | - (7 << 10 /* MAX RX DMA */) | - RTL818X_RX_CONF_BROADCAST | - RTL818X_RX_CONF_NICMAC; - - priv->rx_conf = reg; - rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; - rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; - rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); - - reg = RTL818X_TX_CONF_CW_MIN | - (7 << 21 /* MAX TX DMA */) | - RTL818X_TX_CONF_NO_ICV; - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg |= RTL818X_CMD_TX_ENABLE; - reg |= RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - INIT_DELAYED_WORK(&priv->work, rtl8187_work); - -rtl8187_start_exit: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static void rtl8187_stop(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - struct sk_buff *skb; - u32 reg; - - mutex_lock(&priv->conf_mutex); - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= ~RTL818X_CMD_TX_ENABLE; - reg &= ~RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - priv->rf->stop(dev); - rtl8187_set_anaparam(priv, false); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); - rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - while ((skb = skb_dequeue(&priv->b_tx_status.queue))) - dev_kfree_skb_any(skb); - - usb_kill_anchored_urbs(&priv->anchored); - mutex_unlock(&priv->conf_mutex); - - if (!priv->is_rtl8187b) - cancel_delayed_work_sync(&priv->work); -} - -static int rtl8187_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - int ret = -EOPNOTSUPP; - - mutex_lock(&priv->conf_mutex); - if (priv->vif) - goto exit; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - default: - goto exit; - } - - ret = 0; - priv->vif = vif; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->MAC[i], - ((u8 *)vif->addr)[i]); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - -exit: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static void rtl8187_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8187_priv *priv = dev->priv; - mutex_lock(&priv->conf_mutex); - priv->vif = NULL; - mutex_unlock(&priv->conf_mutex); -} - -static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) -{ - struct rtl8187_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - u32 reg; - - mutex_lock(&priv->conf_mutex); - reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); - /* Enable TX loopback on MAC level to avoid TX during channel - * changes, as this has be seen to causes problems and the - * card will stop work until next reset - */ - rtl818x_iowrite32(priv, &priv->map->TX_CONF, - reg | RTL818X_TX_CONF_LOOPBACK_MAC); - priv->rf->set_chan(dev, conf); - msleep(10); - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); - - rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); - rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); - rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); - rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); - mutex_unlock(&priv->conf_mutex); - return 0; -} - -/* - * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for - * example. Thus we have to use raw values for AC_*_PARAM register addresses. - */ -static __le32 *rtl8187b_ac_addr[4] = { - (__le32 *) 0xFFF0, /* AC_VO */ - (__le32 *) 0xFFF4, /* AC_VI */ - (__le32 *) 0xFFFC, /* AC_BK */ - (__le32 *) 0xFFF8, /* AC_BE */ -}; - -#define SIFS_TIME 0xa - -static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, - bool use_short_preamble) -{ - if (priv->is_rtl8187b) { - u8 difs, eifs; - u16 ack_timeout; - int queue; - - if (use_short_slot) { - priv->slot_time = 0x9; - difs = 0x1c; - eifs = 0x53; - } else { - priv->slot_time = 0x14; - difs = 0x32; - eifs = 0x5b; - } - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); - rtl818x_iowrite8(priv, &priv->map->DIFS, difs); - - /* - * BRSR+1 on 8187B is in fact EIFS register - * Value in units of 4 us - */ - rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs); - - /* - * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout - * register. In units of 4 us like eifs register - * ack_timeout = ack duration + plcp + difs + preamble - */ - ack_timeout = 112 + 48 + difs; - if (use_short_preamble) - ack_timeout += 72; - else - ack_timeout += 144; - rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, - DIV_ROUND_UP(ack_timeout, 4)); - - for (queue = 0; queue < 4; queue++) - rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], - priv->aifsn[queue] * priv->slot_time + - SIFS_TIME); - } else { - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - if (use_short_slot) { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); - rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); - } else { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); - rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); - } - } -} - -static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - u8 reg; - - if (changed & BSS_CHANGED_BSSID) { - mutex_lock(&priv->conf_mutex); - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], - info->bssid[i]); - - if (priv->is_rtl8187b) - reg = RTL818X_MSR_ENEDCA; - else - reg = 0; - - if (is_valid_ether_addr(info->bssid)) - reg |= RTL818X_MSR_INFRA; - else - reg |= RTL818X_MSR_NO_LINK; - - rtl818x_iowrite8(priv, &priv->map->MSR, reg); - - mutex_unlock(&priv->conf_mutex); - } - - if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) - rtl8187_conf_erp(priv, info->use_short_slot, - info->use_short_preamble); -} - -static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, - struct netdev_hw_addr_list *mc_list) -{ - return netdev_hw_addr_list_count(mc_list); -} - -static void rtl8187_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct rtl8187_priv *priv = dev->priv; - - if (changed_flags & FIF_FCSFAIL) - priv->rx_conf ^= RTL818X_RX_CONF_FCS; - if (changed_flags & FIF_CONTROL) - priv->rx_conf ^= RTL818X_RX_CONF_CTRL; - if (changed_flags & FIF_OTHER_BSS) - priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || multicast > 0) - priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; - else - priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; - - *total_flags = 0; - - if (priv->rx_conf & RTL818X_RX_CONF_FCS) - *total_flags |= FIF_FCSFAIL; - if (priv->rx_conf & RTL818X_RX_CONF_CTRL) - *total_flags |= FIF_CONTROL; - if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) - *total_flags |= FIF_OTHER_BSS; - if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) - *total_flags |= FIF_ALLMULTI; - - rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); -} - -static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cw_min, cw_max; - - if (queue > 3) - return -EINVAL; - - cw_min = fls(params->cw_min); - cw_max = fls(params->cw_max); - - if (priv->is_rtl8187b) { - priv->aifsn[queue] = params->aifs; - - /* - * This is the structure of AC_*_PARAM registers in 8187B: - * - TXOP limit field, bit offset = 16 - * - ECWmax, bit offset = 12 - * - ECWmin, bit offset = 8 - * - AIFS, bit offset = 0 - */ - rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], - (params->txop << 16) | (cw_max << 12) | - (cw_min << 8) | (params->aifs * - priv->slot_time + SIFS_TIME)); - } else { - if (queue != 0) - return -EINVAL; - - rtl818x_iowrite8(priv, &priv->map->CW_VAL, - cw_min | (cw_max << 4)); - } - return 0; -} - -static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - - return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | - (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; -} - -static const struct ieee80211_ops rtl8187_ops = { - .tx = rtl8187_tx, - .start = rtl8187_start, - .stop = rtl8187_stop, - .add_interface = rtl8187_add_interface, - .remove_interface = rtl8187_remove_interface, - .config = rtl8187_config, - .bss_info_changed = rtl8187_bss_info_changed, - .prepare_multicast = rtl8187_prepare_multicast, - .configure_filter = rtl8187_configure_filter, - .conf_tx = rtl8187_conf_tx, - .rfkill_poll = rtl8187_rfkill_poll, - .get_tsf = rtl8187_get_tsf, -}; - -static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8187_priv *priv = dev->priv; - u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - - eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; - eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; - eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; - eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; -} - -static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8187_priv *priv = dev->priv; - u8 reg = RTL818X_EEPROM_CMD_PROGRAM; - - if (eeprom->reg_data_in) - reg |= RTL818X_EEPROM_CMD_WRITE; - if (eeprom->reg_data_out) - reg |= RTL818X_EEPROM_CMD_READ; - if (eeprom->reg_data_clock) - reg |= RTL818X_EEPROM_CMD_CK; - if (eeprom->reg_chip_select) - reg |= RTL818X_EEPROM_CMD_CS; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); - udelay(10); -} - -static int __devinit rtl8187_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct ieee80211_hw *dev; - struct rtl8187_priv *priv; - struct eeprom_93cx6 eeprom; - struct ieee80211_channel *channel; - const char *chip_name; - u16 txpwr, reg; - u16 product_id = le16_to_cpu(udev->descriptor.idProduct); - int err, i; - u8 mac_addr[ETH_ALEN]; - - dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); - if (!dev) { - printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); - return -ENOMEM; - } - - priv = dev->priv; - priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B); - - /* allocate "DMA aware" buffer for register accesses */ - priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL); - if (!priv->io_dmabuf) { - err = -ENOMEM; - goto err_free_dev; - } - mutex_init(&priv->io_mutex); - - SET_IEEE80211_DEV(dev, &intf->dev); - usb_set_intfdata(intf, dev); - priv->udev = udev; - - usb_get_dev(udev); - - skb_queue_head_init(&priv->rx_queue); - - BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); - - memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); - memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); - priv->map = (struct rtl818x_csr *)0xFF00; - - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - - dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_RX_INCLUDES_FCS; - - eeprom.data = dev; - eeprom.register_read = rtl8187_eeprom_register_read; - eeprom.register_write = rtl8187_eeprom_register_write; - if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) - eeprom.width = PCI_EEPROM_WIDTH_93C66; - else - eeprom.width = PCI_EEPROM_WIDTH_93C46; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - udelay(10); - - eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR, - (__le16 __force *)mac_addr, 3); - if (!is_valid_ether_addr(mac_addr)) { - printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly " - "generated MAC address\n"); - random_ether_addr(mac_addr); - } - SET_IEEE80211_PERM_ADDR(dev, mac_addr); - - channel = priv->channels; - for (i = 0; i < 3; i++) { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - for (i = 0; i < 2; i++) { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, - &priv->txpwr_base); - - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); - /* 0 means asic B-cut, we should use SW 3 wire - * bit-by-bit banging for radio. 1 means we can use - * USB specific request to write radio registers */ - priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3; - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - if (!priv->is_rtl8187b) { - u32 reg32; - reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF); - reg32 &= RTL818X_TX_CONF_HWVER_MASK; - switch (reg32) { - case RTL818X_TX_CONF_R8187vD_B: - /* Some RTL8187B devices have a USB ID of 0x8187 - * detect them here */ - chip_name = "RTL8187BvB(early)"; - priv->is_rtl8187b = 1; - priv->hw_rev = RTL8187BvB; - break; - case RTL818X_TX_CONF_R8187vD: - chip_name = "RTL8187vD"; - break; - default: - chip_name = "RTL8187vB (default)"; - } - } else { - /* - * Force USB request to write radio registers for 8187B, Realtek - * only uses it in their sources - */ - /*if (priv->asic_rev == 0) { - printk(KERN_WARNING "rtl8187: Forcing use of USB " - "requests to write to radio registers\n"); - priv->asic_rev = 1; - }*/ - switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) { - case RTL818X_R8187B_B: - chip_name = "RTL8187BvB"; - priv->hw_rev = RTL8187BvB; - break; - case RTL818X_R8187B_D: - chip_name = "RTL8187BvD"; - priv->hw_rev = RTL8187BvD; - break; - case RTL818X_R8187B_E: - chip_name = "RTL8187BvE"; - priv->hw_rev = RTL8187BvE; - break; - default: - chip_name = "RTL8187BvB (default)"; - priv->hw_rev = RTL8187BvB; - } - } - - if (!priv->is_rtl8187b) { - for (i = 0; i < 2; i++) { - eeprom_93cx6_read(&eeprom, - RTL8187_EEPROM_TXPWR_CHAN_6 + i, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - } else { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - - eeprom_93cx6_read(&eeprom, 0x0A, &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - - eeprom_93cx6_read(&eeprom, 0x1C, &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - /* Handle the differing rfkill GPIO bit in different models */ - priv->rfkill_mask = RFKILL_MASK_8187_89_97; - if (product_id == 0x8197 || product_id == 0x8198) { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); - if (reg & 0xFF00) - priv->rfkill_mask = RFKILL_MASK_8198; - } - - /* - * XXX: Once this driver supports anything that requires - * beacons it must implement IEEE80211_TX_CTL_ASSIGN_SEQ. - */ - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - - if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) - printk(KERN_INFO "rtl8187: inconsistency between id with OEM" - " info!\n"); - - priv->rf = rtl8187_detect_rf(dev); - dev->extra_tx_headroom = (!priv->is_rtl8187b) ? - sizeof(struct rtl8187_tx_hdr) : - sizeof(struct rtl8187b_tx_hdr); - if (!priv->is_rtl8187b) - dev->queues = 1; - else - dev->queues = 4; - - err = ieee80211_register_hw(dev); - if (err) { - printk(KERN_ERR "rtl8187: Cannot register device\n"); - goto err_free_dmabuf; - } - mutex_init(&priv->conf_mutex); - skb_queue_head_init(&priv->b_tx_status.queue); - - wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", - mac_addr, chip_name, priv->asic_rev, priv->rf->name, - priv->rfkill_mask); - -#ifdef CONFIG_RTL8187_LEDS - eeprom_93cx6_read(&eeprom, 0x3F, ®); - reg &= 0xFF; - rtl8187_leds_init(dev, reg); -#endif - rtl8187_rfkill_init(dev); - - return 0; - - err_free_dmabuf: - kfree(priv->io_dmabuf); - err_free_dev: - ieee80211_free_hw(dev); - usb_set_intfdata(intf, NULL); - usb_put_dev(udev); - return err; -} - -static void __devexit rtl8187_disconnect(struct usb_interface *intf) -{ - struct ieee80211_hw *dev = usb_get_intfdata(intf); - struct rtl8187_priv *priv; - - if (!dev) - return; - -#ifdef CONFIG_RTL8187_LEDS - rtl8187_leds_exit(dev); -#endif - rtl8187_rfkill_exit(dev); - ieee80211_unregister_hw(dev); - - priv = dev->priv; - usb_reset_device(priv->udev); - usb_put_dev(interface_to_usbdev(intf)); - kfree(priv->io_dmabuf); - ieee80211_free_hw(dev); -} - -static struct usb_driver rtl8187_driver = { - .name = KBUILD_MODNAME, - .id_table = rtl8187_table, - .probe = rtl8187_probe, - .disconnect = __devexit_p(rtl8187_disconnect), -}; - -static int __init rtl8187_init(void) -{ - return usb_register(&rtl8187_driver); -} - -static void __exit rtl8187_exit(void) -{ - usb_deregister(&rtl8187_driver); -} - -module_init(rtl8187_init); -module_exit(rtl8187_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c deleted file mode 100644 index 4637337d5ce6..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Linux LED driver for RTL8187 - * - * Copyright 2009 Larry Finger - * - * Based on the LED handling in the r8187 driver, which is: - * Copyright (c) Realtek Semiconductor Corp. All rights reserved. - * - * Thanks to Realtek for their support! - * - * 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. - */ - -#ifdef CONFIG_RTL8187_LEDS - -#include -#include -#include - -#include "rtl8187.h" -#include "rtl8187_leds.h" - -static void led_turn_on(struct work_struct *work) -{ - /* As this routine does read/write operations on the hardware, it must - * be run from a work queue. - */ - u8 reg; - struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, - led_on.work); - struct rtl8187_led *led = &priv->led_tx; - - /* Don't change the LED, when the device is down. */ - if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) - return ; - - /* Skip if the LED is not registered. */ - if (!led->dev) - return; - mutex_lock(&priv->conf_mutex); - switch (led->ledpin) { - case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); - break; - case LED_PIN_LED0: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_LED1: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_HW: - default: - break; - } - mutex_unlock(&priv->conf_mutex); -} - -static void led_turn_off(struct work_struct *work) -{ - /* As this routine does read/write operations on the hardware, it must - * be run from a work queue. - */ - u8 reg; - struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, - led_off.work); - struct rtl8187_led *led = &priv->led_tx; - - /* Don't change the LED, when the device is down. */ - if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) - return ; - - /* Skip if the LED is not registered. */ - if (!led->dev) - return; - mutex_lock(&priv->conf_mutex); - switch (led->ledpin) { - case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); - break; - case LED_PIN_LED0: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_LED1: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_HW: - default: - break; - } - mutex_unlock(&priv->conf_mutex); -} - -/* Callback from the LED subsystem. */ -static void rtl8187_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, - led_dev); - struct ieee80211_hw *hw = led->dev; - struct rtl8187_priv *priv; - static bool radio_on; - - if (!hw) - return; - priv = hw->priv; - if (led->is_radio) { - if (brightness == LED_FULL) { - ieee80211_queue_delayed_work(hw, &priv->led_on, 0); - radio_on = true; - } else if (radio_on) { - radio_on = false; - cancel_delayed_work_sync(&priv->led_on); - ieee80211_queue_delayed_work(hw, &priv->led_off, 0); - } - } else if (radio_on) { - if (brightness == LED_OFF) { - ieee80211_queue_delayed_work(hw, &priv->led_off, 0); - /* The LED is off for 1/20 sec - it just blinks. */ - ieee80211_queue_delayed_work(hw, &priv->led_on, - HZ / 20); - } else - ieee80211_queue_delayed_work(hw, &priv->led_on, 0); - } -} - -static int rtl8187_register_led(struct ieee80211_hw *dev, - struct rtl8187_led *led, const char *name, - const char *default_trigger, u8 ledpin, - bool is_radio) -{ - int err; - struct rtl8187_priv *priv = dev->priv; - - if (led->dev) - return -EEXIST; - if (!default_trigger) - return -EINVAL; - led->dev = dev; - led->ledpin = ledpin; - led->is_radio = is_radio; - strncpy(led->name, name, sizeof(led->name)); - - led->led_dev.name = led->name; - led->led_dev.default_trigger = default_trigger; - led->led_dev.brightness_set = rtl8187_led_brightness_set; - - err = led_classdev_register(&priv->udev->dev, &led->led_dev); - if (err) { - printk(KERN_INFO "LEDs: Failed to register %s\n", name); - led->dev = NULL; - return err; - } - return 0; -} - -static void rtl8187_unregister_led(struct rtl8187_led *led) -{ - struct ieee80211_hw *hw = led->dev; - struct rtl8187_priv *priv = hw->priv; - - led_classdev_unregister(&led->led_dev); - flush_delayed_work(&priv->led_off); - led->dev = NULL; -} - -void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) -{ - struct rtl8187_priv *priv = dev->priv; - char name[RTL8187_LED_MAX_NAME_LEN + 1]; - u8 ledpin; - int err; - - /* According to the vendor driver, the LED operation depends on the - * customer ID encoded in the EEPROM - */ - printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); - switch (custid) { - case EEPROM_CID_RSVD0: - case EEPROM_CID_RSVD1: - case EEPROM_CID_SERCOMM_PS: - case EEPROM_CID_QMI: - case EEPROM_CID_DELL: - case EEPROM_CID_TOSHIBA: - ledpin = LED_PIN_GPIO0; - break; - case EEPROM_CID_ALPHA0: - ledpin = LED_PIN_LED0; - break; - case EEPROM_CID_HW: - ledpin = LED_PIN_HW; - break; - default: - ledpin = LED_PIN_GPIO0; - } - - INIT_DELAYED_WORK(&priv->led_on, led_turn_on); - INIT_DELAYED_WORK(&priv->led_off, led_turn_off); - - snprintf(name, sizeof(name), - "rtl8187-%s::radio", wiphy_name(dev->wiphy)); - err = rtl8187_register_led(dev, &priv->led_radio, name, - ieee80211_get_radio_led_name(dev), ledpin, true); - if (err) - return; - - snprintf(name, sizeof(name), - "rtl8187-%s::tx", wiphy_name(dev->wiphy)); - err = rtl8187_register_led(dev, &priv->led_tx, name, - ieee80211_get_tx_led_name(dev), ledpin, false); - if (err) - goto err_tx; - - snprintf(name, sizeof(name), - "rtl8187-%s::rx", wiphy_name(dev->wiphy)); - err = rtl8187_register_led(dev, &priv->led_rx, name, - ieee80211_get_rx_led_name(dev), ledpin, false); - if (!err) - return; - - /* registration of RX LED failed - unregister */ - rtl8187_unregister_led(&priv->led_tx); -err_tx: - rtl8187_unregister_led(&priv->led_radio); -} - -void rtl8187_leds_exit(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - - rtl8187_unregister_led(&priv->led_radio); - rtl8187_unregister_led(&priv->led_rx); - rtl8187_unregister_led(&priv->led_tx); - cancel_delayed_work_sync(&priv->led_off); - cancel_delayed_work_sync(&priv->led_on); -} -#endif /* def CONFIG_RTL8187_LEDS */ - diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.h b/drivers/net/wireless/rtl818x/rtl8187_leds.h deleted file mode 100644 index d743c96d4a20..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Definitions for RTL8187 leds - * - * Copyright 2009 Larry Finger - * - * Based on the LED handling in the r8187 driver, which is: - * Copyright (c) Realtek Semiconductor Corp. All rights reserved. - * - * 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. - */ - -#ifndef RTL8187_LED_H -#define RTL8187_LED_H - -#ifdef CONFIG_RTL8187_LEDS - -#define RTL8187_LED_MAX_NAME_LEN 21 - -#include -#include - -enum { - LED_PIN_LED0, - LED_PIN_LED1, - LED_PIN_GPIO0, - LED_PIN_HW -}; - -enum { - EEPROM_CID_RSVD0 = 0x00, - EEPROM_CID_RSVD1 = 0xFF, - EEPROM_CID_ALPHA0 = 0x01, - EEPROM_CID_SERCOMM_PS = 0x02, - EEPROM_CID_HW = 0x03, - EEPROM_CID_TOSHIBA = 0x04, - EEPROM_CID_QMI = 0x07, - EEPROM_CID_DELL = 0x08 -}; - -struct rtl8187_led { - struct ieee80211_hw *dev; - /* The LED class device */ - struct led_classdev led_dev; - /* The pin/method used to control the led */ - u8 ledpin; - /* The unique name string for this LED device. */ - char name[RTL8187_LED_MAX_NAME_LEN + 1]; - /* If the LED is radio or tx/rx */ - bool is_radio; -}; - -void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); -void rtl8187_leds_exit(struct ieee80211_hw *dev); - -#endif /* def CONFIG_RTL8187_LEDS */ - -#endif /* RTL8187_LED_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c deleted file mode 100644 index 03555e1e0cab..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Linux RFKILL support for RTL8187 - * - * Copyright (c) 2009 Herton Ronaldo Krzesinski - * - * Based on the RFKILL handling in the r8187 driver, which is: - * Copyright (c) Realtek Semiconductor Corp. All rights reserved. - * - * Thanks to Realtek for their support! - * - * 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 -#include -#include - -#include "rtl8187.h" -#include "rtl8187_rfkill.h" - -static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) -{ - u8 gpio; - - gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask); - gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - - return gpio & priv->rfkill_mask; -} - -void rtl8187_rfkill_init(struct ieee80211_hw *hw) -{ - struct rtl8187_priv *priv = hw->priv; - - priv->rfkill_off = rtl8187_is_radio_enabled(priv); - printk(KERN_INFO "rtl8187: wireless switch is %s\n", - priv->rfkill_off ? "on" : "off"); - wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); - wiphy_rfkill_start_polling(hw->wiphy); -} - -void rtl8187_rfkill_poll(struct ieee80211_hw *hw) -{ - bool enabled; - struct rtl8187_priv *priv = hw->priv; - - mutex_lock(&priv->conf_mutex); - enabled = rtl8187_is_radio_enabled(priv); - if (unlikely(enabled != priv->rfkill_off)) { - priv->rfkill_off = enabled; - printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", - enabled ? "on" : "off"); - wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); - } - mutex_unlock(&priv->conf_mutex); -} - -void rtl8187_rfkill_exit(struct ieee80211_hw *hw) -{ - wiphy_rfkill_stop_polling(hw->wiphy); -} diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.h b/drivers/net/wireless/rtl818x/rtl8187_rfkill.h deleted file mode 100644 index e12575e96d11..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef RTL8187_RFKILL_H -#define RTL8187_RFKILL_H - -void rtl8187_rfkill_init(struct ieee80211_hw *hw); -void rtl8187_rfkill_poll(struct ieee80211_hw *hw); -void rtl8187_rfkill_exit(struct ieee80211_hw *hw); - -#endif /* RTL8187_RFKILL_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c deleted file mode 100644 index 5c6666f09ac1..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - * Radio tuning for RTL8225 on RTL8187 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * Magic delays, register offsets, and phy value tables below are - * taken from the original r8187 driver sources. Thanks to Realtek - * for their support! - * - * 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 -#include -#include - -#include "rtl8187.h" -#include "rtl8187_rtl8225.h" - -static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data) -{ - struct rtl8187_priv *priv = dev->priv; - u16 reg80, reg84, reg82; - u32 bangdata; - int i; - - bangdata = (data << 4) | (addr & 0xf); - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); - - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - udelay(10); - - for (i = 15; i >= 0; i--) { - u16 reg = reg80 | (bangdata & (1 << i)) >> i; - - if (i & 1) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - - if (!(i & 1)) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); -} - -static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data) -{ - struct rtl8187_priv *priv = dev->priv; - u16 reg80, reg82, reg84; - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - - reg80 &= ~(0x3 << 2); - reg84 &= ~0xF; - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - udelay(10); - - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits16 = data; - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - addr, 0x8225, &priv->io_dmabuf->bits16, sizeof(data), - HZ / 2); - - mutex_unlock(&priv->io_mutex); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); -} - -static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) -{ - struct rtl8187_priv *priv = dev->priv; - - if (priv->asic_rev) - rtl8225_write_8051(dev, addr, cpu_to_le16(data)); - else - rtl8225_write_bitbang(dev, addr, data); -} - -static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) -{ - struct rtl8187_priv *priv = dev->priv; - u16 reg80, reg82, reg84, out; - int i; - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - - reg80 &= ~0xF; - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(4); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - udelay(5); - - for (i = 4; i >= 0; i--) { - u16 reg = reg80 | ((addr >> i) & 1); - - if (!(i & 1)) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - udelay(1); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - udelay(2); - - if (i & 1) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - udelay(1); - } - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(2); - - out = 0; - for (i = 11; i >= 0; i--) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(1); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - - if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) - out |= 1 << i; - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(2); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 2)); - udelay(2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); - - return out; -} - -static const u16 rtl8225bcd_rxgain[] = { - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, - 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, - 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, - 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, - 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, - 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, - 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, - 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, - 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, - 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, - 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb -}; - -static const u8 rtl8225_agc[] = { - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, - 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, - 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, - 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, - 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, - 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, - 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, - 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, - 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, - 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 -}; - -static const u8 rtl8225_gain[] = { - 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */ - 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */ - 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */ - 0x33, 0x80, 0x79, 0xc5, /* -78dBm */ - 0x43, 0x78, 0x76, 0xc5, /* -74dBm */ - 0x53, 0x60, 0x73, 0xc5, /* -70dBm */ - 0x63, 0x58, 0x70, 0xc5, /* -66dBm */ -}; - -static const u8 rtl8225_threshold[] = { - 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd -}; - -static const u8 rtl8225_tx_gain_cck_ofdm[] = { - 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e -}; - -static const u8 rtl8225_tx_power_cck[] = { - 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, - 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, - 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, - 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, - 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, - 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 -}; - -static const u8 rtl8225_tx_power_cck_ch14[] = { - 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225_tx_power_ofdm[] = { - 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 -}; - -static const u32 rtl8225_chan[] = { - 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, - 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 -}; - -static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - u32 reg; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xF; - ofdm_power = priv->channels[channel - 1].hw_value >> 4; - - cck_power = min(cck_power, (u8)11); - if (ofdm_power > (u8)15) - ofdm_power = 25; - else - ofdm_power += 10; - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); - - if (channel == 14) - tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; - else - tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - msleep(1); // FIXME: optional? - - /* anaparam2 on */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, - RTL8187_RTL8225_ANAPARAM2_ON); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl8225_write_phy_ofdm(dev, 2, 0x42); - rtl8225_write_phy_ofdm(dev, 6, 0x00); - rtl8225_write_phy_ofdm(dev, 8, 0x00); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1); - - tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; - - rtl8225_write_phy_ofdm(dev, 5, *tmp); - rtl8225_write_phy_ofdm(dev, 7, *tmp); - - msleep(1); -} - -static void rtl8225_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - - rtl8225_write(dev, 0x0, 0x067); - rtl8225_write(dev, 0x1, 0xFE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x486); - rtl8225_write(dev, 0x5, 0xBC0); - rtl8225_write(dev, 0x6, 0xAE6); - rtl8225_write(dev, 0x7, 0x82A); - rtl8225_write(dev, 0x8, 0x01F); - rtl8225_write(dev, 0x9, 0x334); - rtl8225_write(dev, 0xA, 0xFD4); - rtl8225_write(dev, 0xB, 0x391); - rtl8225_write(dev, 0xC, 0x050); - rtl8225_write(dev, 0xD, 0x6DB); - rtl8225_write(dev, 0xE, 0x029); - rtl8225_write(dev, 0xF, 0x914); msleep(100); - - rtl8225_write(dev, 0x2, 0xC4D); msleep(200); - rtl8225_write(dev, 0x2, 0x44D); msleep(200); - - if (!(rtl8225_read(dev, 6) & (1 << 7))) { - rtl8225_write(dev, 0x02, 0x0c4d); - msleep(200); - rtl8225_write(dev, 0x02, 0x044d); - msleep(100); - if (!(rtl8225_read(dev, 6) & (1 << 7))) - wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", - rtl8225_read(dev, 6)); - } - - rtl8225_write(dev, 0x0, 0x127); - - for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); - } - - rtl8225_write(dev, 0x0, 0x027); - rtl8225_write(dev, 0x0, 0x22F); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); - rtl8225_write_phy_ofdm(dev, 0x02, 0x42); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); - rtl8225_write_phy_ofdm(dev, 0x06, 0x40); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); - rtl8225_write_phy_ofdm(dev, 0x08, 0x40); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); - rtl8225_write_phy_ofdm(dev, 0x11, 0x06); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); - rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); - rtl8225_write_phy_ofdm(dev, 0x21, 0x27); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); - rtl8225_write_phy_ofdm(dev, 0x25, 0x20); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); - - rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); - rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); - rtl8225_write_phy_cck(dev, 0x03, 0x20); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); - rtl8225_write_phy_cck(dev, 0x05, 0x12); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); - rtl8225_write_phy_cck(dev, 0x07, 0x78); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); - rtl8225_write_phy_cck(dev, 0x10, 0x9b); - rtl8225_write_phy_cck(dev, 0x11, 0x88); - rtl8225_write_phy_cck(dev, 0x12, 0x47); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8d); - rtl8225_write_phy_cck(dev, 0x42, 0x15); - rtl8225_write_phy_cck(dev, 0x43, 0x18); - rtl8225_write_phy_cck(dev, 0x44, 0x1f); - rtl8225_write_phy_cck(dev, 0x45, 0x1e); - rtl8225_write_phy_cck(dev, 0x46, 0x1a); - rtl8225_write_phy_cck(dev, 0x47, 0x15); - rtl8225_write_phy_cck(dev, 0x48, 0x10); - rtl8225_write_phy_cck(dev, 0x49, 0x0a); - rtl8225_write_phy_cck(dev, 0x4a, 0x05); - rtl8225_write_phy_cck(dev, 0x4b, 0x02); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); - - rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); - - rtl8225_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); - - /* set sensitivity */ - rtl8225_write(dev, 0x0c, 0x50); - rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); - rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); - rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]); -} - -static const u8 rtl8225z2_agc[] = { - 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, - 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, - 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, - 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, - 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, - 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 -}; -static const u8 rtl8225z2_ofdm[] = { - 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, - 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, - 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, - 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, - 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f, - 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, - 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, - 0x6d, 0x3c, 0xfb, 0x07 -}; - -static const u8 rtl8225z2_tx_power_cck_ch14[] = { - 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225z2_tx_power_cck[] = { - 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, - 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, - 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, - 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 -}; - -static const u8 rtl8225z2_tx_power_ofdm[] = { - 0x42, 0x00, 0x40, 0x00, 0x40 -}; - -static const u8 rtl8225z2_tx_gain_cck_ofdm[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 -}; - -static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - u32 reg; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xF; - ofdm_power = priv->channels[channel - 1].hw_value >> 4; - - cck_power = min(cck_power, (u8)15); - cck_power += priv->txpwr_base & 0xF; - cck_power = min(cck_power, (u8)35); - - if (ofdm_power > (u8)15) - ofdm_power = 25; - else - ofdm_power += 10; - ofdm_power += priv->txpwr_base >> 4; - ofdm_power = min(ofdm_power, (u8)35); - - if (channel == 14) - tmp = rtl8225z2_tx_power_cck_ch14; - else - tmp = rtl8225z2_tx_power_cck; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225z2_tx_gain_cck_ofdm[cck_power]); - msleep(1); - - /* anaparam2 on */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, - RTL8187_RTL8225_ANAPARAM2_ON); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl8225_write_phy_ofdm(dev, 2, 0x42); - rtl8225_write_phy_ofdm(dev, 5, 0x00); - rtl8225_write_phy_ofdm(dev, 6, 0x40); - rtl8225_write_phy_ofdm(dev, 7, 0x00); - rtl8225_write_phy_ofdm(dev, 8, 0x40); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225z2_tx_gain_cck_ofdm[ofdm_power]); - msleep(1); -} - -static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xF; - ofdm_power = priv->channels[channel - 1].hw_value >> 4; - - if (cck_power > 15) - cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22; - else - cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7; - cck_power += priv->txpwr_base & 0xF; - cck_power = min(cck_power, (u8)35); - - if (ofdm_power > 15) - ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25; - else - ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10; - ofdm_power += (priv->txpwr_base >> 4) & 0xF; - ofdm_power = min(ofdm_power, (u8)35); - - if (channel == 14) - tmp = rtl8225z2_tx_power_cck_ch14; - else - tmp = rtl8225z2_tx_power_cck; - - if (priv->hw_rev == RTL8187BvB) { - if (cck_power <= 6) - ; /* do nothing */ - else if (cck_power <= 11) - tmp += 8; - else - tmp += 16; - } else { - if (cck_power <= 5) - ; /* do nothing */ - else if (cck_power <= 11) - tmp += 8; - else if (cck_power <= 17) - tmp += 16; - else - tmp += 24; - } - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225z2_tx_gain_cck_ofdm[cck_power] << 1); - msleep(1); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1); - if (priv->hw_rev == RTL8187BvB) { - if (ofdm_power <= 11) { - rtl8225_write_phy_ofdm(dev, 0x87, 0x60); - rtl8225_write_phy_ofdm(dev, 0x89, 0x60); - } else { - rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); - rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); - } - } else { - if (ofdm_power <= 11) { - rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); - rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); - } else if (ofdm_power <= 17) { - rtl8225_write_phy_ofdm(dev, 0x87, 0x54); - rtl8225_write_phy_ofdm(dev, 0x89, 0x54); - } else { - rtl8225_write_phy_ofdm(dev, 0x87, 0x50); - rtl8225_write_phy_ofdm(dev, 0x89, 0x50); - } - } - msleep(1); -} - -static const u16 rtl8225z2_rxgain[] = { - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, - 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, - 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, - 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, - 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, - 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, - 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, - 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, - 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, - 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, - 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb -}; - -static const u8 rtl8225z2_gain_bg[] = { - 0x23, 0x15, 0xa5, /* -82-1dBm */ - 0x23, 0x15, 0xb5, /* -82-2dBm */ - 0x23, 0x15, 0xc5, /* -82-3dBm */ - 0x33, 0x15, 0xc5, /* -78dBm */ - 0x43, 0x15, 0xc5, /* -74dBm */ - 0x53, 0x15, 0xc5, /* -70dBm */ - 0x63, 0x15, 0xc5 /* -66dBm */ -}; - -static void rtl8225z2_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - - rtl8225_write(dev, 0x0, 0x2BF); - rtl8225_write(dev, 0x1, 0xEE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x8C3); - rtl8225_write(dev, 0x5, 0xC72); - rtl8225_write(dev, 0x6, 0x0E6); - rtl8225_write(dev, 0x7, 0x82A); - rtl8225_write(dev, 0x8, 0x03F); - rtl8225_write(dev, 0x9, 0x335); - rtl8225_write(dev, 0xa, 0x9D4); - rtl8225_write(dev, 0xb, 0x7BB); - rtl8225_write(dev, 0xc, 0x850); - rtl8225_write(dev, 0xd, 0xCDF); - rtl8225_write(dev, 0xe, 0x02B); - rtl8225_write(dev, 0xf, 0x114); - msleep(100); - - rtl8225_write(dev, 0x0, 0x1B7); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); - } - - rtl8225_write(dev, 0x3, 0x080); - rtl8225_write(dev, 0x5, 0x004); - rtl8225_write(dev, 0x0, 0x0B7); - rtl8225_write(dev, 0x2, 0xc4D); - - msleep(200); - rtl8225_write(dev, 0x2, 0x44D); - msleep(100); - - if (!(rtl8225_read(dev, 6) & (1 << 7))) { - rtl8225_write(dev, 0x02, 0x0C4D); - msleep(200); - rtl8225_write(dev, 0x02, 0x044D); - msleep(100); - if (!(rtl8225_read(dev, 6) & (1 << 7))) - wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", - rtl8225_read(dev, 6)); - } - - msleep(200); - - rtl8225_write(dev, 0x0, 0x2BF); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); - rtl8225_write_phy_ofdm(dev, 0x02, 0x42); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); - rtl8225_write_phy_ofdm(dev, 0x06, 0x40); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); - rtl8225_write_phy_ofdm(dev, 0x08, 0x40); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); - rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); - rtl8225_write_phy_ofdm(dev, 0x11, 0x07); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); - rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); - rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); - rtl8225_write_phy_ofdm(dev, 0x21, 0x17); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); - rtl8225_write_phy_ofdm(dev, 0x23, 0x80); - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); - rtl8225_write_phy_ofdm(dev, 0x25, 0x00); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); - - rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]); - rtl8225_write_phy_ofdm(dev, 0x21, 0x37); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); - rtl8225_write_phy_cck(dev, 0x03, 0x20); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); - rtl8225_write_phy_cck(dev, 0x05, 0x12); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); - rtl8225_write_phy_cck(dev, 0x07, 0x78); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); - rtl8225_write_phy_cck(dev, 0x10, 0x9b); - rtl8225_write_phy_cck(dev, 0x11, 0x88); - rtl8225_write_phy_cck(dev, 0x12, 0x47); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8d); - rtl8225_write_phy_cck(dev, 0x42, 0x15); - rtl8225_write_phy_cck(dev, 0x43, 0x18); - rtl8225_write_phy_cck(dev, 0x44, 0x36); - rtl8225_write_phy_cck(dev, 0x45, 0x35); - rtl8225_write_phy_cck(dev, 0x46, 0x2e); - rtl8225_write_phy_cck(dev, 0x47, 0x25); - rtl8225_write_phy_cck(dev, 0x48, 0x1c); - rtl8225_write_phy_cck(dev, 0x49, 0x12); - rtl8225_write_phy_cck(dev, 0x4a, 0x09); - rtl8225_write_phy_cck(dev, 0x4b, 0x04); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); - - rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1); - - rtl8225z2_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); -} - -static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - - rtl8225_write(dev, 0x0, 0x0B7); - rtl8225_write(dev, 0x1, 0xEE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x8C3); - rtl8225_write(dev, 0x5, 0xC72); - rtl8225_write(dev, 0x6, 0x0E6); - rtl8225_write(dev, 0x7, 0x82A); - rtl8225_write(dev, 0x8, 0x03F); - rtl8225_write(dev, 0x9, 0x335); - rtl8225_write(dev, 0xa, 0x9D4); - rtl8225_write(dev, 0xb, 0x7BB); - rtl8225_write(dev, 0xc, 0x850); - rtl8225_write(dev, 0xd, 0xCDF); - rtl8225_write(dev, 0xe, 0x02B); - rtl8225_write(dev, 0xf, 0x114); - - rtl8225_write(dev, 0x0, 0x1B7); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); - } - - rtl8225_write(dev, 0x3, 0x080); - rtl8225_write(dev, 0x5, 0x004); - rtl8225_write(dev, 0x0, 0x0B7); - - rtl8225_write(dev, 0x2, 0xC4D); - - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x0, 0x2BF); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03); - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07); - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); - - rtl8225_write_phy_ofdm(dev, 0x80, 0x12); - for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]); - rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i); - rtl8225_write_phy_ofdm(dev, 0xE, 0); - } - rtl8225_write_phy_ofdm(dev, 0x80, 0x10); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++) - rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]); - - rtl8225_write_phy_ofdm(dev, 0x97, 0x46); - rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); - rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); - rtl8225_write_phy_cck(dev, 0xc1, 0x88); -} - -static void rtl8225_rf_stop(struct ieee80211_hw *dev) -{ - rtl8225_write(dev, 0x4, 0x1f); -} - -static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8187_priv *priv = dev->priv; - int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); - - if (priv->rf->init == rtl8225_rf_init) - rtl8225_rf_set_tx_power(dev, chan); - else if (priv->rf->init == rtl8225z2_rf_init) - rtl8225z2_rf_set_tx_power(dev, chan); - else - rtl8225z2_b_rf_set_tx_power(dev, chan); - - rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); - msleep(10); -} - -static const struct rtl818x_rf_ops rtl8225_ops = { - .name = "rtl8225", - .init = rtl8225_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel -}; - -static const struct rtl818x_rf_ops rtl8225z2_ops = { - .name = "rtl8225z2", - .init = rtl8225z2_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel -}; - -static const struct rtl818x_rf_ops rtl8225z2_b_ops = { - .name = "rtl8225z2", - .init = rtl8225z2_b_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel -}; - -const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev) -{ - u16 reg8, reg9; - struct rtl8187_priv *priv = dev->priv; - - if (!priv->is_rtl8187b) { - rtl8225_write(dev, 0, 0x1B7); - - reg8 = rtl8225_read(dev, 8); - reg9 = rtl8225_read(dev, 9); - - rtl8225_write(dev, 0, 0x0B7); - - if (reg8 != 0x588 || reg9 != 0x700) - return &rtl8225_ops; - - return &rtl8225z2_ops; - } else - return &rtl8225z2_b_ops; -} diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h deleted file mode 100644 index 20c5b6ead0f6..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Radio tuning definitions for RTL8225 on RTL8187 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * 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. - */ - -#ifndef RTL8187_RTL8225_H -#define RTL8187_RTL8225_H - -#define RTL8187_RTL8225_ANAPARAM_ON 0xa0000a59 -#define RTL8187_RTL8225_ANAPARAM2_ON 0x860c7312 -#define RTL8187_RTL8225_ANAPARAM_OFF 0xa00beb59 -#define RTL8187_RTL8225_ANAPARAM2_OFF 0x840dec11 - -#define RTL8187B_RTL8225_ANAPARAM_ON 0x45090658 -#define RTL8187B_RTL8225_ANAPARAM2_ON 0x727f3f52 -#define RTL8187B_RTL8225_ANAPARAM3_ON 0x00 -#define RTL8187B_RTL8225_ANAPARAM_OFF 0x55480658 -#define RTL8187B_RTL8225_ANAPARAM2_OFF 0x72003f50 -#define RTL8187B_RTL8225_ANAPARAM3_OFF 0x00 - -const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *); - -static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, - u8 addr, u32 data) -{ - rtl8187_write_phy(dev, addr, data); -} - -static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, - u8 addr, u32 data) -{ - rtl8187_write_phy(dev, addr, data | 0x10000); -} - -#endif /* RTL8187_RTL8225_H */ -- cgit v1.2.1