1
linux/drivers/net/wireless/rtl8187_rtl8225.c
Johannes Berg 8318d78a44 cfg80211 API for channels/bitrates, mac80211 and driver conversion
This patch creates new cfg80211 wiphy API for channel and bitrate
registration and converts mac80211 and drivers to the new API. The
old mac80211 API is completely ripped out. All drivers (except ath5k)
are updated to the new API, in many cases I expect that optimisations
can be done.

Along with the regulatory code I've also ripped out the
IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED flag, I believe it to be
unnecessary if the hardware simply gives us whatever channels it wants
to support and we then enable/disable them as required, which is pretty
much required for travelling.

Additionally, the patch adds proper "basic" rate handling for STA
mode interface, AP mode interface will have to have new API added
to allow userspace to set the basic rate set, currently it'll be
empty... However, the basic rate handling will need to be moved to
the BSS conf stuff.

I do expect there to be bugs in this, especially wrt. transmit
power handling where I'm basically clueless about how it should work.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-02-29 15:19:32 -05:00

779 lines
26 KiB
C

/*
* Radio tuning for RTL8225 on RTL8187
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
* Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
*
* Based on the r8187 driver, which is:
* Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
*
* Magic delays, register offsets, and phy value tables below are
* taken from the original r8187 driver sources. Thanks to Realtek
* for their support!
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/usb.h>
#include <net/mac80211.h>
#include "rtl8187.h"
#include "rtl8187_rtl8225.h"
static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data)
{
struct rtl8187_priv *priv = dev->priv;
u16 reg80, reg84, reg82;
u32 bangdata;
int i;
bangdata = (data << 4) | (addr & 0xf);
reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7);
udelay(10);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
udelay(10);
for (i = 15; i >= 0; i--) {
u16 reg = reg80 | (bangdata & (1 << i)) >> i;
if (i & 1)
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
if (!(i & 1))
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
}
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
udelay(10);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
msleep(2);
}
static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data)
{
struct rtl8187_priv *priv = dev->priv;
u16 reg80, reg82, reg84;
reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
reg80 &= ~(0x3 << 2);
reg84 &= ~0xF;
rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007);
rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007);
udelay(10);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
udelay(10);
usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
addr, 0x8225, &data, sizeof(data), HZ / 2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
udelay(10);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
msleep(2);
}
static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data)
{
struct rtl8187_priv *priv = dev->priv;
if (priv->asic_rev)
rtl8225_write_8051(dev, addr, cpu_to_le16(data));
else
rtl8225_write_bitbang(dev, addr, data);
}
static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr)
{
struct rtl8187_priv *priv = dev->priv;
u16 reg80, reg82, reg84, out;
int i;
reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
reg80 &= ~0xF;
rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
udelay(4);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
udelay(5);
for (i = 4; i >= 0; i--) {
u16 reg = reg80 | ((addr >> i) & 1);
if (!(i & 1)) {
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
udelay(1);
}
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg | (1 << 1));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg | (1 << 1));
udelay(2);
if (i & 1) {
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
udelay(1);
}
}
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3) | (1 << 1));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3));
udelay(2);
out = 0;
for (i = 11; i >= 0; i--) {
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3));
udelay(1);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3) | (1 << 1));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3) | (1 << 1));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3) | (1 << 1));
udelay(2);
if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
out |= 1 << i;
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3));
udelay(2);
}
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
reg80 | (1 << 3) | (1 << 2));
udelay(2);
rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
return out;
}
static const u16 rtl8225bcd_rxgain[] = {
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
};
static const u8 rtl8225_agc[] = {
0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};
static const u8 rtl8225_gain[] = {
0x23, 0x88, 0x7c, 0xa5, /* -82dBm */
0x23, 0x88, 0x7c, 0xb5, /* -82dBm */
0x23, 0x88, 0x7c, 0xc5, /* -82dBm */
0x33, 0x80, 0x79, 0xc5, /* -78dBm */
0x43, 0x78, 0x76, 0xc5, /* -74dBm */
0x53, 0x60, 0x73, 0xc5, /* -70dBm */
0x63, 0x58, 0x70, 0xc5, /* -66dBm */
};
static const u8 rtl8225_threshold[] = {
0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
};
static const u8 rtl8225_tx_gain_cck_ofdm[] = {
0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
};
static const u8 rtl8225_tx_power_cck[] = {
0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
};
static const u8 rtl8225_tx_power_cck_ch14[] = {
0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
};
static const u8 rtl8225_tx_power_ofdm[] = {
0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
};
static const u32 rtl8225_chan[] = {
0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
};
static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
{
struct rtl8187_priv *priv = dev->priv;
u8 cck_power, ofdm_power;
const u8 *tmp;
u32 reg;
int i;
cck_power = priv->channels[channel - 1].hw_value & 0xF;
ofdm_power = priv->channels[channel - 1].hw_value >> 4;
cck_power = min(cck_power, (u8)11);
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);
rtl8225_write_phy_ofdm(dev, 2, 0x42);
rtl8225_write_phy_ofdm(dev, 6, 0x00);
rtl8225_write_phy_ofdm(dev, 8, 0x00);
rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1);
tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
rtl8225_write_phy_ofdm(dev, 5, *tmp);
rtl8225_write_phy_ofdm(dev, 7, *tmp);
msleep(1);
}
static void rtl8225_rf_init(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
int i;
rtl8225_write(dev, 0x0, 0x067); msleep(1);
rtl8225_write(dev, 0x1, 0xFE0); msleep(1);
rtl8225_write(dev, 0x2, 0x44D); msleep(1);
rtl8225_write(dev, 0x3, 0x441); msleep(1);
rtl8225_write(dev, 0x4, 0x486); msleep(1);
rtl8225_write(dev, 0x5, 0xBC0); msleep(1);
rtl8225_write(dev, 0x6, 0xAE6); msleep(1);
rtl8225_write(dev, 0x7, 0x82A); msleep(1);
rtl8225_write(dev, 0x8, 0x01F); msleep(1);
rtl8225_write(dev, 0x9, 0x334); msleep(1);
rtl8225_write(dev, 0xA, 0xFD4); msleep(1);
rtl8225_write(dev, 0xB, 0x391); msleep(1);
rtl8225_write(dev, 0xC, 0x050); msleep(1);
rtl8225_write(dev, 0xD, 0x6DB); msleep(1);
rtl8225_write(dev, 0xE, 0x029); msleep(1);
rtl8225_write(dev, 0xF, 0x914); msleep(100);
rtl8225_write(dev, 0x2, 0xC4D); msleep(200);
rtl8225_write(dev, 0x2, 0x44D); msleep(200);
if (!(rtl8225_read(dev, 6) & (1 << 7))) {
rtl8225_write(dev, 0x02, 0x0c4d);
msleep(200);
rtl8225_write(dev, 0x02, 0x044d);
msleep(100);
if (!(rtl8225_read(dev, 6) & (1 << 7)))
printk(KERN_WARNING "%s: RF Calibration Failed! %x\n",
wiphy_name(dev->wiphy), rtl8225_read(dev, 6));
}
rtl8225_write(dev, 0x0, 0x127);
for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
rtl8225_write(dev, 0x1, i + 1);
rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
}
rtl8225_write(dev, 0x0, 0x027);
rtl8225_write(dev, 0x0, 0x22F);
for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
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, 0x42); 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, 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, 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, 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_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]);
rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]);
rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]);
rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]);
rtl8225_write_phy_cck(dev, 0x00, 0x98); 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, 0x9b); 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);
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 *)0xFF94, 0x3dc00002);
/* set sensitivity */
rtl8225_write(dev, 0x0c, 0x50);
rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]);
rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]);
rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]);
rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]);
rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]);
}
static const u8 rtl8225z2_tx_power_cck_ch14[] = {
0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
};
static const u8 rtl8225z2_tx_power_cck[] = {
0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
};
static const u8 rtl8225z2_tx_power_ofdm[] = {
0x42, 0x00, 0x40, 0x00, 0x40
};
static const u8 rtl8225z2_tx_gain_cck_ofdm[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
};
static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
{
struct rtl8187_priv *priv = dev->priv;
u8 cck_power, ofdm_power;
const u8 *tmp;
u32 reg;
int i;
cck_power = priv->channels[channel - 1].hw_value & 0xF;
ofdm_power = priv->channels[channel - 1].hw_value >> 4;
cck_power = min(cck_power, (u8)15);
cck_power += priv->txpwr_base & 0xF;
cck_power = min(cck_power, (u8)35);
ofdm_power = min(ofdm_power, (u8)15);
ofdm_power += priv->txpwr_base >> 4;
ofdm_power = min(ofdm_power, (u8)35);
if (channel == 14)
tmp = rtl8225z2_tx_power_cck_ch14;
else
tmp = rtl8225z2_tx_power_cck;
for (i = 0; i < 8; i++)
rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
rtl8225z2_tx_gain_cck_ofdm[cck_power]);
msleep(1);
/* anaparam2 on */
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
rtl8225_write_phy_ofdm(dev, 2, 0x42);
rtl8225_write_phy_ofdm(dev, 5, 0x00);
rtl8225_write_phy_ofdm(dev, 6, 0x40);
rtl8225_write_phy_ofdm(dev, 7, 0x00);
rtl8225_write_phy_ofdm(dev, 8, 0x40);
rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
rtl8225z2_tx_gain_cck_ofdm[ofdm_power]);
msleep(1);
}
static const u16 rtl8225z2_rxgain[] = {
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
};
static const u8 rtl8225z2_gain_bg[] = {
0x23, 0x15, 0xa5, /* -82-1dBm */
0x23, 0x15, 0xb5, /* -82-2dBm */
0x23, 0x15, 0xc5, /* -82-3dBm */
0x33, 0x15, 0xc5, /* -78dBm */
0x43, 0x15, 0xc5, /* -74dBm */
0x53, 0x15, 0xc5, /* -70dBm */
0x63, 0x15, 0xc5 /* -66dBm */
};
static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
int i;
rtl8225_write(dev, 0x0, 0x2BF); 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);
rtl8225_write(dev, 0x0, 0x1B7);
for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
rtl8225_write(dev, 0x1, i + 1);
rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
}
rtl8225_write(dev, 0x3, 0x080);
rtl8225_write(dev, 0x5, 0x004);
rtl8225_write(dev, 0x0, 0x0B7);
rtl8225_write(dev, 0x2, 0xc4D);
msleep(200);
rtl8225_write(dev, 0x2, 0x44D);
msleep(100);
if (!(rtl8225_read(dev, 6) & (1 << 7))) {
rtl8225_write(dev, 0x02, 0x0C4D);
msleep(200);
rtl8225_write(dev, 0x02, 0x044D);
msleep(100);
if (!(rtl8225_read(dev, 6) & (1 << 7)))
printk(KERN_WARNING "%s: RF Calibration Failed! %x\n",
wiphy_name(dev->wiphy), rtl8225_read(dev, 6));
}
msleep(200);
rtl8225_write(dev, 0x0, 0x2BF);
for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
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, 0x42); 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, 0x08); 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, 0x07); 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, 0x15); 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, 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, 0x17); 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, 0x00); msleep(1);
rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]);
rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]);
rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]);
rtl8225_write_phy_ofdm(dev, 0x21, 0x37);
rtl8225_write_phy_cck(dev, 0x00, 0x98); 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, 0x9b); 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, 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 *)0xFF5B, 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 *)0xFF94, 0x3dc00002);
}
static void rtl8225_rf_stop(struct ieee80211_hw *dev)
{
u8 reg;
struct rtl8187_priv *priv = dev->priv;
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 rtl8187_priv *priv = dev->priv;
int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
if (priv->rf->init == rtl8225_rf_init)
rtl8225_rf_set_tx_power(dev, chan);
else
rtl8225z2_rf_set_tx_power(dev, chan);
rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
msleep(10);
}
static const struct rtl818x_rf_ops rtl8225_ops = {
.name = "rtl8225",
.init = rtl8225_rf_init,
.stop = rtl8225_rf_stop,
.set_chan = rtl8225_rf_set_channel
};
static const struct rtl818x_rf_ops rtl8225z2_ops = {
.name = "rtl8225z2",
.init = rtl8225z2_rf_init,
.stop = rtl8225_rf_stop,
.set_chan = rtl8225_rf_set_channel
};
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;
}