diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/Kconfig | 56 | ||||
-rw-r--r-- | drivers/net/wireless/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180.h | 151 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_dev.c | 1048 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_grf5101.c | 179 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_grf5101.h | 28 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_max2820.c | 150 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_max2820.h | 28 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_rtl8225.c | 779 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_rtl8225.h | 23 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_sa2400.c | 201 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8180_sa2400.h | 36 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8187.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8187_dev.c | 60 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8187_rtl8225.c | 52 | ||||
-rw-r--r-- | drivers/net/wireless/rtl8187_rtl8225.h | 9 | ||||
-rw-r--r-- | drivers/net/wireless/rtl818x.h | 29 |
17 files changed, 2774 insertions, 60 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 50a8b6c2fa0..f372960904b 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -545,6 +545,62 @@ config USB_ZD1201 To compile this driver as a module, choose M here: the module will be called zd1201. +config RTL8180 + tristate "Realtek 8180/8185 PCI support" + depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL + select EEPROM_93CX6 + ---help--- + This is a driver for RTL8180 and RTL8185 based cards. + These are PCI based chips found in cards such as: + + (RTL8185 802.11g) + A-Link WL54PC + + (RTL8180 802.11b) + Belkin F5D6020 v3 + Belkin F5D6020 v3 + Dlink DWL-610 + Dlink DWL-510 + Netgear MA521 + Level-One WPC-0101 + Acer Aspire 1357 LMi + VCTnet PC-11B1 + Ovislink AirLive WL-1120PCM + Mentor WL-PCI + Linksys WPC11 v4 + TrendNET TEW-288PI + D-Link DWL-520 Rev D + Repotec RP-WP7126 + TP-Link TL-WN250/251 + Zonet ZEW1000 + Longshine LCS-8031-R + HomeLine HLW-PCC200 + GigaFast WF721-AEX + Planet WL-3553 + Encore ENLWI-PCI1-NT + TrendNET TEW-266PC + Gigabyte GN-WLMR101 + Siemens-fujitsu Amilo D1840W + Edimax EW-7126 + PheeNet WL-11PCIR + Tonze PC-2100T + Planet WL-8303 + Dlink DWL-650 v M1 + Edimax EW-7106 + Q-Tec 770WC + Topcom Skyr@cer 4011b + Roper FreeLan 802.11b (edition 2004) + Wistron Neweb Corp CB-200B + Pentagram HorNET + QTec 775WC + TwinMOS Booming B Series + Micronet SP906BB + Sweex LC700010 + Surecom EP-9428 + Safecom SWLCR-1100 + + Thanks to Realtek for their support! + config RTL8187 tristate "Realtek 8187 USB support" depends on MAC80211 && USB && WLAN_80211 && EXPERIMENTAL diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7e1535ece17..6af7b158624 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -47,7 +47,10 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_ZD1201) += zd1201.o obj-$(CONFIG_LIBERTAS) += libertas/ +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 + +obj-$(CONFIG_RTL8180) += rtl8180.o obj-$(CONFIG_RTL8187) += rtl8187.o obj-$(CONFIG_ADM8211) += adm8211.o diff --git a/drivers/net/wireless/rtl8180.h b/drivers/net/wireless/rtl8180.h new file mode 100644 index 00000000000..0b333cacbd8 --- /dev/null +++ b/drivers/net/wireless/rtl8180.h @@ -0,0 +1,151 @@ +#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) + +enum rtl8180_tx_desc_flags { + RTL8180_TX_DESC_FLAG_NO_ENC = (1 << 15), + RTL8180_TX_DESC_FLAG_TX_OK = (1 << 15), + RTL8180_TX_DESC_FLAG_SPLCP = (1 << 16), + RTL8180_TX_DESC_FLAG_RX_UNDER = (1 << 16), + RTL8180_TX_DESC_FLAG_MOREFRAG = (1 << 17), + RTL8180_TX_DESC_FLAG_CTS = (1 << 18), + RTL8180_TX_DESC_FLAG_RTS = (1 << 23), + RTL8180_TX_DESC_FLAG_LS = (1 << 28), + RTL8180_TX_DESC_FLAG_FS = (1 << 29), + RTL8180_TX_DESC_FLAG_DMA = (1 << 30), + RTL8180_TX_DESC_FLAG_OWN = (1 << 31) +}; + +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]; +} __attribute__ ((packed)); + +enum rtl8180_rx_desc_flags { + RTL8180_RX_DESC_FLAG_ICV_ERR = (1 << 12), + RTL8180_RX_DESC_FLAG_CRC32_ERR = (1 << 13), + RTL8180_RX_DESC_FLAG_PM = (1 << 14), + RTL8180_RX_DESC_FLAG_RX_ERR = (1 << 15), + RTL8180_RX_DESC_FLAG_BCAST = (1 << 16), + RTL8180_RX_DESC_FLAG_PAM = (1 << 17), + RTL8180_RX_DESC_FLAG_MCAST = (1 << 18), + RTL8180_RX_DESC_FLAG_SPLCP = (1 << 25), + RTL8180_RX_DESC_FLAG_FOF = (1 << 26), + RTL8180_RX_DESC_FLAG_DMA_FAIL = (1 << 27), + RTL8180_RX_DESC_FLAG_LS = (1 << 28), + RTL8180_RX_DESC_FLAG_FS = (1 << 29), + RTL8180_RX_DESC_FLAG_EOR = (1 << 30), + RTL8180_RX_DESC_FLAG_OWN = (1 << 31) +}; + +struct rtl8180_rx_desc { + __le32 flags; + __le32 flags2; + union { + __le32 rx_buf; + __le64 tsft; + }; +} __attribute__ ((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_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr __iomem *map; + const struct rtl818x_rf_ops *rf; + int mode; + int if_id; + + /* 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_hw_mode modes[2]; + struct pci_dev *pdev; + u32 rx_conf; + + int r8185; + u32 anaparam; + u16 rfparam; + u8 csthreshold; +}; + +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/rtl8180_dev.c b/drivers/net/wireless/rtl8180_dev.c new file mode 100644 index 00000000000..4b7b032c194 --- /dev/null +++ b/drivers/net/wireless/rtl8180_dev.c @@ -0,0 +1,1048 @@ + +/* + * Linux device driver for RTL8180 / RTL8185 + * + * Copyright 2007 Michael Wu <flamingice@sourmilk.net> + * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, 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 <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/eeprom_93cx6.h> +#include <net/mac80211.h> + +#include "rtl8180.h" +#include "rtl8180_rtl8225.h" +#include "rtl8180_sa2400.h" +#include "rtl8180_max2820.h" +#include "rtl8180_grf5101.h" + +MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); +MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>"); +MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); +MODULE_LICENSE("GPL"); + +static struct pci_device_id rtl8180_table[] __devinitdata = { + /* rtl8185 */ + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, + { 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); + +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; + + 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 & RTL8180_RX_DESC_FLAG_OWN) + return; + + if (unlikely(flags & (RTL8180_RX_DESC_FLAG_DMA_FAIL | + RTL8180_RX_DESC_FLAG_FOF | + RTL8180_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; + /* TODO: improve signal/rssi reporting */ + rx_status.signal = flags2 & 0xFF; + rx_status.ssi = (flags2 >> 8) & 0x7F; + rx_status.rate = (flags >> 20) & 0xF; + rx_status.freq = dev->conf.freq; + rx_status.channel = dev->conf.channel; + rx_status.phymode = dev->conf.phymode; + rx_status.mactime = le64_to_cpu(entry->tsft); + rx_status.flag |= RX_FLAG_TSFT; + if (flags & RTL8180_RX_DESC_FLAG_CRC32_ERR) + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; + + ieee80211_rx_irqsafe(dev, skb, &rx_status); + + 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(RTL8180_RX_DESC_FLAG_OWN | + MAX_RX_SIZE); + if (priv->rx_idx == 31) + entry->flags |= cpu_to_le32(RTL8180_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_status status = { {0} }; + struct ieee80211_tx_control *control; + u32 flags = le32_to_cpu(entry->flags); + + if (flags & RTL8180_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); + + control = *((struct ieee80211_tx_control **)skb->cb); + if (control) + memcpy(&status.control, control, sizeof(*control)); + kfree(control); + + if (!(status.control.flags & IEEE80211_TXCTL_NO_ACK)) { + if (flags & RTL8180_TX_DESC_FLAG_TX_OK) + status.flags = IEEE80211_TX_STATUS_ACK; + else + status.excessive_retries = 1; + } + status.retry_count = flags & 0xFF; + + ieee80211_tx_status_irqsafe(dev, skb, &status); + 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_control *control) +{ + 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; + u16 plcp_len = 0; + __le16 rts_duration = 0; + + prio = control->queue; + ring = &priv->tx_ring[prio]; + + mapping = pci_map_single(priv->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE); + + tx_flags = RTL8180_TX_DESC_FLAG_OWN | RTL8180_TX_DESC_FLAG_FS | + RTL8180_TX_DESC_FLAG_LS | (control->tx_rate << 24) | + (control->rts_cts_rate << 19) | skb->len; + + if (priv->r8185) + tx_flags |= RTL8180_TX_DESC_FLAG_DMA | + RTL8180_TX_DESC_FLAG_NO_ENC; + + if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) + tx_flags |= RTL8180_TX_DESC_FLAG_RTS; + else if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + tx_flags |= RTL8180_TX_DESC_FLAG_CTS; + + *((struct ieee80211_tx_control **) skb->cb) = + kmemdup(control, sizeof(*control), GFP_ATOMIC); + + if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) + rts_duration = ieee80211_rts_duration(dev, priv->if_id, skb->len, control); + + if (!priv->r8185) { + unsigned int remainder; + + plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), + (control->rate->rate * 2) / 10); + remainder = (16 * (skb->len + 4)) % + ((control->rate->rate * 2) / 10); + if (remainder > 0 && remainder <= 6) + plcp_len |= 1 << 15; + } + + spin_lock_irqsave(&priv->lock, flags); + 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 = control->alt_retry_rate != -1 ? + control->alt_retry_rate << 4 : 0; + entry->retry_limit = control->retry_limit; + 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, control->queue); + 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) { + printk(KERN_ERR "%s: reset timeout!\n", wiphy_name(dev->wiphy)); + 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) { + printk(KERN_ERR "%s: Cannot allocate RX ring\n", + wiphy_name(dev->wiphy)); + 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(RTL8180_RX_DESC_FLAG_OWN | + MAX_RX_SIZE); + } + entry->flags |= cpu_to_le32(RTL8180_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) { + printk(KERN_ERR "%s: Cannot allocate TX ring (prio = %d)\n", + wiphy_name(dev->wiphy), 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(*((struct ieee80211_tx_control **) skb->cb)); + 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) { + printk(KERN_ERR "%s: failed to register IRQ handler\n", + wiphy_name(dev->wiphy)); + 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); + + priv->mode = IEEE80211_IF_TYPE_MNTR; + 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; + + priv->mode = IEEE80211_IF_TYPE_INVALID; + + 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 int rtl8180_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + + if (priv->mode != IEEE80211_IF_TYPE_MNTR) + return -EOPNOTSUPP; + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + priv->mode = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], + cpu_to_le32(*(u32 *)conf->mac_addr)); + rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], + cpu_to_le16(*(u16 *)(conf->mac_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_if_init_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + priv->mode = IEEE80211_IF_TYPE_MNTR; +} + +static int rtl8180_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + + priv->rf->set_chan(dev, conf); + + return 0; +} + +static int rtl8180_config_interface(struct ieee80211_hw *dev, int if_id, + struct ieee80211_if_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + priv->if_id = if_id; + + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); + + if (is_valid_ether_addr(conf->bssid)) + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA); + else + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK); + + return 0; +} + +static void rtl8180_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, struct dev_addr_list *mclist) +{ + 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 || mc_count > 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, + .config_interface = rtl8180_config_interface, + .configure_filter = rtl8180_configure_filter, +}; + +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; + DECLARE_MAC_BUF(mac); + + 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, 0xFFFFFF00ULL)) || + (err = pci_set_consistent_dma_mask(pdev, 0xFFFFFF00ULL))) { + 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; + + 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; + } + + memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); + memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); + priv->modes[0].mode = MODE_IEEE80211G; + priv->modes[0].num_rates = ARRAY_SIZE(rtl818x_rates); + priv->modes[0].rates = priv->rates; + priv->modes[0].num_channels = ARRAY_SIZE(rtl818x_channels); + priv->modes[0].channels = priv->channels; + priv->modes[1].mode = MODE_IEEE80211B; + priv->modes[1].num_rates = 4; + priv->modes[1].rates = priv->rates; + priv->modes[1].num_channels = ARRAY_SIZE(rtl818x_channels); + priv->modes[1].channels = priv->channels; + priv->mode = IEEE80211_IF_TYPE_INVALID; + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_RX_INCLUDES_FCS; + dev->queues = 1; + dev->max_rssi = 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) { + if ((err = ieee80211_register_hwmode(dev, &priv->modes[0]))) + goto err_iounmap; + + pci_try_set_mwi(pdev); + } + + if ((err = ieee80211_register_hwmode(dev, &priv->modes[1]))) + goto err_iounmap; + + 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 *)dev->wiphy->perm_addr, 3); + if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { + printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" + " randomly generated MAC addr\n", pci_name(pdev)); + random_ether_addr(dev->wiphy->perm_addr); + } + + /* CCK TX power */ + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); + priv->channels[i].val = txpwr & 0xFF; + priv->channels[i + 1].val = 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].val |= (txpwr & 0xFF) << 8; + priv->channels[i + 1].val |= 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; + } + + printk(KERN_INFO "%s: hwaddr %s, %s + %s\n", + wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_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/rtl8180_grf5101.c b/drivers/net/wireless/rtl8180_grf5101.c new file mode 100644 index 00000000000..8293e19c4c5 --- /dev/null +++ b/drivers/net/wireless/rtl8180_grf5101.c @@ -0,0 +1,179 @@ + +/* + * Radio tuning for GCT GRF5101 on RTL8180 + * + * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * + * 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 <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <net/mac80211.h> + +#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 void grf5101_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + u32 txpw = priv->channels[conf->channel - 1].val & 0xFF; + u32 chan = conf->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, chan); +} + +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 +}; diff --git a/drivers/net/wireless/rtl8180_grf5101.h b/drivers/net/wireless/rtl8180_grf5101.h new file mode 100644 index 00000000000..76647111bcf --- /dev/null +++ b/drivers/net/wireless/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 <andreamrl@tiscali.it> + * + * 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/rtl8180_max2820.c b/drivers/net/wireless/rtl8180_max2820.c new file mode 100644 index 00000000000..98fe9fd6496 --- /dev/null +++ b/drivers/net/wireless/rtl8180_max2820.c @@ -0,0 +1,150 @@ +/* + * Radio tuning for Maxim max2820 on RTL8180 + * + * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * + * 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 <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <net/mac80211.h> + +#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 void max2820_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + unsigned int chan_idx = conf ? conf->channel - 1 : 0; + u32 txpw = priv->channels[chan_idx].val & 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, chan); + 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 +}; diff --git a/drivers/net/wireless/rtl8180_max2820.h b/drivers/net/wireless/rtl8180_max2820.h new file mode 100644 index 00000000000..61cf6d1e7d5 --- /dev/null +++ b/drivers/net/wireless/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 <andreamrl@tiscali.it> + * + * 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/rtl8180_rtl8225.c b/drivers/net/wireless/rtl8180_rtl8225.c new file mode 100644 index 00000000000..ef3832bee85 --- /dev/null +++ b/drivers/net/wireless/rtl8180_rtl8225.c @@ -0,0 +1,779 @@ + +/* + * Radio tuning for RTL8225 on RTL8180 + * + * Copyright 2007 Michael Wu <flamingice@sourmilk.net> + * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * + * Based on the r8180 driver, which is: + * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, 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 <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <net/mac80211.h> + +#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 | !!(bangdata & (1 << 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)); + 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].val & 0xFF; + ofdm_power = priv->channels[channel - 1].val >> 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].val & 0xFF; + ofdm_power = priv->channels[channel - 1].val >> 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; + + if (priv->rf->init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, conf->channel); + else + rtl8225z2_rf_set_tx_power(dev, conf->channel); + + rtl8225_write(dev, 0x7, rtl8225_chan[conf->channel - 1]); + msleep(10); + + if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) { + 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 +}; + +static const struct rtl818x_rf_ops rtl8225z2_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +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/rtl8180_rtl8225.h b/drivers/net/wireless/rtl8180_rtl8225.h new file mode 100644 index 00000000000..310013a2d72 --- /dev/null +++ b/drivers/net/wireless/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/rtl8180_sa2400.c b/drivers/net/wireless/rtl8180_sa2400.c new file mode 100644 index 00000000000..e08ace7b1cb --- /dev/null +++ b/drivers/net/wireless/rtl8180_sa2400.c @@ -0,0 +1,201 @@ + +/* + * Radio tuning for Philips SA2400 on RTL8180 + * + * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * + * 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 <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <net/mac80211.h> + +#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 void sa2400_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + u32 txpw = priv->channels[conf->channel - 1].val & 0xFF; + u32 chan = sa2400_chan[conf->channel - 1]; + + write_sa2400(dev, 7, txpw); + + sa2400_write_phy_antenna(dev, chan); + + 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 +}; diff --git a/drivers/net/wireless/rtl8180_sa2400.h b/drivers/net/wireless/rtl8180_sa2400.h new file mode 100644 index 00000000000..a4aaa0d413f --- /dev/null +++ b/drivers/net/wireless/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 <andreamrl@tiscali.it> + * + * 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/rtl8187.h b/drivers/net/wireless/rtl8187.h index 6ad322ef0da..26a4f1097b4 100644 --- a/drivers/net/wireless/rtl8187.h +++ b/drivers/net/wireless/rtl8187.h @@ -64,7 +64,7 @@ struct rtl8187_tx_hdr { struct rtl8187_priv { /* common between rtl818x drivers */ struct rtl818x_csr *map; - void (*rf_init)(struct ieee80211_hw *); + const struct rtl818x_rf_ops *rf; int mode; int if_id; diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 09e48d3bef3..8dab25a6599 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -393,37 +393,19 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); msleep(100); - priv->rf_init(dev); + priv->rf->init(dev); rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - reg = rtl818x_ioread16(priv, &priv->map->PGSELECT) & 0xfffe; - rtl818x_iowrite16(priv, &priv->map->PGSELECT, reg | 0x1); + 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_iowrite16(priv, &priv->map->PGSELECT, reg); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); return 0; } -static void rtl8187_set_channel(struct ieee80211_hw *dev, int channel) -{ - u32 reg; - struct rtl8187_priv *priv = dev->priv; - - 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); - msleep(10); - rtl8225_rf_set_channel(dev, channel); - msleep(10); - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); -} - static int rtl8187_start(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; @@ -492,7 +474,7 @@ static void rtl8187_stop(struct ieee80211_hw *dev) reg &= ~RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); - rtl8225_rf_stop(dev); + priv->rf->stop(dev); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); @@ -543,7 +525,19 @@ static void rtl8187_remove_interface(struct ieee80211_hw *dev, static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { struct rtl8187_priv *priv = dev->priv; - rtl8187_set_channel(dev, conf->channel); + u32 reg; + + 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); + msleep(10); + priv->rf->set_chan(dev, conf); + msleep(10); + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); @@ -753,23 +747,16 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, &priv->txpwr_base); - reg = rtl818x_ioread16(priv, &priv->map->PGSELECT) & ~1; - rtl818x_iowrite16(priv, &priv->map->PGSELECT, reg | 1); + 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_iowrite16(priv, &priv->map->PGSELECT, reg); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - rtl8225_write(dev, 0, 0x1B7); - - if (rtl8225_read(dev, 8) != 0x588 || rtl8225_read(dev, 9) != 0x700) - priv->rf_init = rtl8225_rf_init; - else - priv->rf_init = rtl8225z2_rf_init; - - rtl8225_write(dev, 0, 0x0B7); + priv->rf = rtl8187_detect_rf(dev); err = ieee80211_register_hw(dev); if (err) { @@ -779,8 +766,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, printk(KERN_INFO "%s: hwaddr %s, rtl8187 V%d + %s\n", wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr), - priv->asic_rev, priv->rf_init == rtl8225_rf_init ? - "rtl8225" : "rtl8225z2"); + priv->asic_rev, priv->rf->name); return 0; diff --git a/drivers/net/wireless/rtl8187_rtl8225.c b/drivers/net/wireless/rtl8187_rtl8225.c index efc41207780..b713de17ba0 100644 --- a/drivers/net/wireless/rtl8187_rtl8225.c +++ b/drivers/net/wireless/rtl8187_rtl8225.c @@ -101,7 +101,7 @@ static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data) msleep(2); } -void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) +static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) { struct rtl8187_priv *priv = dev->priv; @@ -111,7 +111,7 @@ void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) rtl8225_write_bitbang(dev, addr, data); } -u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) +static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) { struct rtl8187_priv *priv = dev->priv; u16 reg80, reg82, reg84, out; @@ -325,7 +325,7 @@ static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) msleep(1); } -void rtl8225_rf_init(struct ieee80211_hw *dev) +static void rtl8225_rf_init(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; int i; @@ -567,7 +567,7 @@ static const u8 rtl8225z2_gain_bg[] = { 0x63, 0x15, 0xc5 /* -66dBm */ }; -void rtl8225z2_rf_init(struct ieee80211_hw *dev) +static void rtl8225z2_rf_init(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; int i; @@ -715,7 +715,7 @@ void rtl8225z2_rf_init(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); } -void rtl8225_rf_stop(struct ieee80211_hw *dev) +static void rtl8225_rf_stop(struct ieee80211_hw *dev) { u8 reg; struct rtl8187_priv *priv = dev->priv; @@ -731,15 +731,47 @@ void rtl8225_rf_stop(struct ieee80211_hw *dev) rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } -void rtl8225_rf_set_channel(struct ieee80211_hw *dev, int channel) +static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) { struct rtl8187_priv *priv = dev->priv; - if (priv->rf_init == rtl8225_rf_init) - rtl8225_rf_set_tx_power(dev, channel); + if (priv->rf->init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, conf->channel); else - rtl8225z2_rf_set_tx_power(dev, channel); + rtl8225z2_rf_set_tx_power(dev, conf->channel); - rtl8225_write(dev, 0x7, rtl8225_chan[channel - 1]); + rtl8225_write(dev, 0x7, rtl8225_chan[conf->channel - 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 +}; + +const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev) +{ + u16 reg8, reg9; + + 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/rtl8187_rtl8225.h b/drivers/net/wireless/rtl8187_rtl8225.h index 798ba4a9737..d39ed0295b6 100644 --- a/drivers/net/wireless/rtl8187_rtl8225.h +++ b/drivers/net/wireless/rtl8187_rtl8225.h @@ -20,14 +20,7 @@ #define RTL8225_ANAPARAM_OFF 0xa00beb59 #define RTL8225_ANAPARAM2_OFF 0x840dec11 -void rtl8225_write(struct ieee80211_hw *, u8 addr, u16 data); -u16 rtl8225_read(struct ieee80211_hw *, u8 addr); - -void rtl8225_rf_init(struct ieee80211_hw *); -void rtl8225z2_rf_init(struct ieee80211_hw *); -void rtl8225_rf_stop(struct ieee80211_hw *); -void rtl8225_rf_set_channel(struct ieee80211_hw *, int); - +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) diff --git a/drivers/net/wireless/rtl818x.h b/drivers/net/wireless/rtl818x.h index 880d4becae3..1e7d6f8278d 100644 --- a/drivers/net/wireless/rtl818x.h +++ b/drivers/net/wireless/rtl818x.h @@ -58,13 +58,17 @@ struct rtl818x_csr { #define RTL818X_INT_TX_FO (1 << 15) __le32 TX_CONF; #define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17) +#define RTL818X_TX_CONF_LOOPBACK_CONT (3 << 17) #define RTL818X_TX_CONF_NO_ICV (1 << 19) #define RTL818X_TX_CONF_DISCW (1 << 20) +#define RTL818X_TX_CONF_SAT_HWPLCP (1 << 24) #define RTL818X_TX_CONF_R8180_ABCD (2 << 25) #define RTL818X_TX_CONF_R8180_F (3 << 25) #define RTL818X_TX_CONF_R8185_ABC (4 << 25) #define RTL818X_TX_CONF_R8185_D (5 << 25) #define RTL818X_TX_CONF_HWVER_MASK (7 << 25) +#define RTL818X_TX_CONF_PROBE_DTS (1 << 29) +#define RTL818X_TX_CONF_HW_SEQNUM (1 << 30) #define RTL818X_TX_CONF_CW_MIN (1 << 31) __le32 RX_CONF; #define RTL818X_RX_CONF_MONITOR (1 << 0) @@ -75,8 +79,12 @@ struct rtl818x_csr { #define RTL818X_RX_CONF_DATA (1 << 18) #define RTL818X_RX_CONF_CTRL (1 << 19) #define RTL818X_RX_CONF_MGMT (1 << 20) +#define RTL818X_RX_CONF_ADDR3 (1 << 21) +#define RTL818X_RX_CONF_PM (1 << 22) #define RTL818X_RX_CONF_BSSID (1 << 23) #define RTL818X_RX_CONF_RX_AUTORESETPHY (1 << 28) +#define RTL818X_RX_CONF_CSDM1 (1 << 29) +#define RTL818X_RX_CONF_CSDM2 (1 << 30) #define RTL818X_RX_CONF_ONLYERLPKT (1 << 31) __le32 INT_TIMEOUT; __le32 TBDA; @@ -92,6 +100,7 @@ struct rtl818x_csr { u8 CONFIG0; u8 CONFIG1; u8 CONFIG2; +#define RTL818X_CONFIG2_ANTENNA_DIV (1 << 6) __le32 ANAPARAM; u8 MSR; #define RTL818X_MSR_NO_LINK (0 << 2) @@ -104,14 +113,17 @@ struct rtl818x_csr { #define RTL818X_CONFIG4_VCOOFF (1 << 7) u8 TESTR; u8 reserved_9[2]; - __le16 PGSELECT; + u8 PGSELECT; + u8 SECURITY; __le32 ANAPARAM2; u8 reserved_10[12]; __le16 BEACON_INTERVAL; __le16 ATIM_WND; __le16 BEACON_INTERVAL_TIME; __le16 ATIMTR_INTERVAL; - u8 reserved_11[4]; + u8 PHY_DELAY; + u8 CARRIER_SENSE_COUNTER; + u8 reserved_11[2]; u8 PHY[4]; __le16 RFPinsOutput; __le16 RFPinsEnable; @@ -149,11 +161,20 @@ struct rtl818x_csr { u8 RETRY_CTR; u8 reserved_18[5]; __le32 RDSAR; - u8 reserved_19[18]; - u16 TALLY_CNT; + u8 reserved_19[12]; + __le16 FEMR; + u8 reserved_20[4]; + __le16 TALLY_CNT; u8 TALLY_SEL; } __attribute__((packed)); +struct rtl818x_rf_ops { + char *name; + void (*init)(struct ieee80211_hw *); + void (*stop)(struct ieee80211_hw *); + void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *); +}; + static const struct ieee80211_rate rtl818x_rates[] = { { .rate = 10, .val = 0, |