// SPDX-License-Identifier: GPL-2.0-only
/*
* Radio tuning for Philips SA2400 on RTL8180
*
* Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* 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!
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include <net/mac80211.h>
#include "rtl8180.h"
#include "sa2400.h"
static const u32 sa2400_chan[] = {
0 x00096c, /* ch1 */
0 x080970,
0 x100974,
0 x180978,
0 x000980,
0 x080984,
0 x100988,
0 x18098c,
0 x000994,
0 x080998,
0 x10099c,
0 x1809a0,
0 x0009a8,
0 x0009b4, /* 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 = 0 xb0000000;
phy_config |= ((u32)(addr & 0 xf)) << 24 ;
phy_config |= data & 0 xffffff;
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, 0 x10, ant);
}
static u8 sa2400_rf_rssi_map[] = {
0 x64, 0 x64, 0 x63, 0 x62, 0 x61, 0 x60, 0 x5f, 0 x5e,
0 x5d, 0 x5c, 0 x5b, 0 x5a, 0 x57, 0 x54, 0 x52, 0 x50,
0 x4e, 0 x4c, 0 x4a, 0 x48, 0 x46, 0 x44, 0 x41, 0 x3f,
0 x3c, 0 x3a, 0 x37, 0 x36, 0 x36, 0 x1c, 0 x1c, 0 x1b,
0 x1b, 0 x1a, 0 x1a, 0 x19, 0 x19, 0 x18, 0 x18, 0 x17,
0 x17, 0 x16, 0 x16, 0 x15, 0 x15, 0 x14, 0 x14, 0 x13,
0 x13, 0 x12, 0 x12, 0 x11, 0 x11, 0 x10, 0 x10, 0 x0f,
0 x0f, 0 x0e, 0 x0e, 0 x0d, 0 x0d, 0 x0c, 0 x0c, 0 x0b,
0 x0b, 0 x0a, 0 x0a, 0 x09, 0 x09, 0 x08, 0 x08, 0 x07,
0 x07, 0 x06, 0 x06, 0 x05, 0 x04, 0 x03, 0 x02,
};
static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq)
{
if (sq == 0 x80)
return 1 ;
if (sq > 78 )
return 32 ;
/* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */
return 65 * sa2400_rf_rssi_map[sq] / 100 ;
}
static void sa2400_rf_set_channel(struct ieee80211_hw *dev,
struct ieee80211_conf *conf)
{
struct rtl8180_priv *priv = dev->priv;
int channel =
ieee80211_frequency_to_channel(conf->chandef.chan->center_freq);
u32 txpw = priv->channels[channel - 1 ].hw_value & 0 xFF;
u32 chan = sa2400_chan[channel - 1 ];
write_sa2400(dev, 7 , txpw);
sa2400_write_phy_antenna(dev, channel);
write_sa2400(dev, 0 , chan);
write_sa2400(dev, 1 , 0 xbb50);
write_sa2400(dev, 2 , 0 x80);
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 , 0 xbb50);
write_sa2400(dev, 2 , 0 x80);
write_sa2400(dev, 3 , 0 );
write_sa2400(dev, 4 , 0 x19340 | firdac);
write_sa2400(dev, 5 , 0 x1dfb | (SA2400_MAX_SENS - 54 ) << 15 );
write_sa2400(dev, 4 , 0 x19348 | firdac); /* calibrate VCO */
if (!analogphy)
write_sa2400(dev, 4 , 0 x1938c); /*???*/
write_sa2400(dev, 4 , 0 x19340 | firdac);
write_sa2400(dev, 0 , sa2400_chan[0 ]);
write_sa2400(dev, 1 , 0 xbb50);
write_sa2400(dev, 2 , 0 x80);
write_sa2400(dev, 3 , 0 );
write_sa2400(dev, 4 , 0 x19344 | firdac); /* calibrate filter */
/* new from rtl8180 embedded driver (rtl8181 project) */
write_sa2400(dev, 6 , 0 x13ff | (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 , 0 x19341); /* calibrates DC */
/* a 5us sleep is required here,
* we rely on the 3ms delay introduced in write_sa2400 */
write_sa2400(dev, 4 , 0 x19345);
/* 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 , 0 x19341 | firdac); /* RTX MODE */
/* baseband configuration */
rtl8180_write_phy(dev, 0 , 0 x98);
rtl8180_write_phy(dev, 3 , 0 x38);
rtl8180_write_phy(dev, 4 , 0 xe0);
rtl8180_write_phy(dev, 5 , 0 x90);
rtl8180_write_phy(dev, 6 , 0 x1a);
rtl8180_write_phy(dev, 7 , 0 x64);
sa2400_write_phy_antenna(dev, 1 );
rtl8180_write_phy(dev, 0 x11, 0 x80);
if (rtl818x_ioread8(priv, &priv->map->CONFIG2) &
RTL818X_CONFIG2_ANTENNA_DIV)
rtl8180_write_phy(dev, 0 x12, 0 xc7); /* enable ant diversity */
else
rtl8180_write_phy(dev, 0 x12, 0 x47); /* disable ant diversity */
rtl8180_write_phy(dev, 0 x13, 0 x90 | priv->csthreshold);
rtl8180_write_phy(dev, 0 x19, 0 x0);
rtl8180_write_phy(dev, 0 x1a, 0 xa0);
}
const struct rtl818x_rf_ops sa2400_rf_ops = {
.name = "Philips" ,
.init = sa2400_rf_init,
.stop = sa2400_rf_stop,
.set_chan = sa2400_rf_set_channel,
.calc_rssi = sa2400_rf_calc_rssi,
};
Messung V0.5 in Prozent C=96 H=91 G=93
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland