18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Radio tuning for Maxim max2820 on RTL8180 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Code from the BSD driver and the rtl8181 project have been 88c2ecf20Sopenharmony_ci * very useful to understand certain things 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * I want to thanks the Authors of such projects and the Ndiswrapper 118c2ecf20Sopenharmony_ci * project Authors. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * A special Big Thanks also is for all people who donated me cards, 148c2ecf20Sopenharmony_ci * making possible the creation of the original rtl8180 driver 158c2ecf20Sopenharmony_ci * from which this code is derived! 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <net/mac80211.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "rtl8180.h" 238c2ecf20Sopenharmony_ci#include "max2820.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const u32 max2820_chan[] = { 268c2ecf20Sopenharmony_ci 12, /* CH 1 */ 278c2ecf20Sopenharmony_ci 17, 288c2ecf20Sopenharmony_ci 22, 298c2ecf20Sopenharmony_ci 27, 308c2ecf20Sopenharmony_ci 32, 318c2ecf20Sopenharmony_ci 37, 328c2ecf20Sopenharmony_ci 42, 338c2ecf20Sopenharmony_ci 47, 348c2ecf20Sopenharmony_ci 52, 358c2ecf20Sopenharmony_ci 57, 368c2ecf20Sopenharmony_ci 62, 378c2ecf20Sopenharmony_ci 67, 388c2ecf20Sopenharmony_ci 72, 398c2ecf20Sopenharmony_ci 84, /* CH 14 */ 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void write_max2820(struct ieee80211_hw *dev, u8 addr, u32 data) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct rtl8180_priv *priv = dev->priv; 458c2ecf20Sopenharmony_ci u32 phy_config; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci phy_config = 0x90 + (data & 0xf); 488c2ecf20Sopenharmony_ci phy_config <<= 16; 498c2ecf20Sopenharmony_ci phy_config += addr; 508c2ecf20Sopenharmony_ci phy_config <<= 8; 518c2ecf20Sopenharmony_ci phy_config += (data >> 4) & 0xff; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, 548c2ecf20Sopenharmony_ci (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci msleep(1); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct rtl8180_priv *priv = dev->priv; 628c2ecf20Sopenharmony_ci u8 ant; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ant = MAXIM_ANTENNA; 658c2ecf20Sopenharmony_ci if (priv->rfparam & RF_PARAM_ANTBDEFAULT) 668c2ecf20Sopenharmony_ci ant |= BB_ANTENNA_B; 678c2ecf20Sopenharmony_ci if (chan == 14) 688c2ecf20Sopenharmony_ci ant |= BB_ANTATTEN_CHAN14; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x10, ant); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic u8 max2820_rf_calc_rssi(u8 agc, u8 sq) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci bool odd; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci odd = !!(agc & 1); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci agc >>= 1; 808c2ecf20Sopenharmony_ci if (odd) 818c2ecf20Sopenharmony_ci agc += 76; 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci agc += 66; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* TODO: change addends above to avoid mult / div below */ 868c2ecf20Sopenharmony_ci return 65 * agc / 100; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void max2820_rf_set_channel(struct ieee80211_hw *dev, 908c2ecf20Sopenharmony_ci struct ieee80211_conf *conf) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct rtl8180_priv *priv = dev->priv; 938c2ecf20Sopenharmony_ci int channel = conf ? 948c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(conf->chandef.chan->center_freq) : 1; 958c2ecf20Sopenharmony_ci unsigned int chan_idx = channel - 1; 968c2ecf20Sopenharmony_ci u32 txpw = priv->channels[chan_idx].hw_value & 0xFF; 978c2ecf20Sopenharmony_ci u32 chan = max2820_chan[chan_idx]; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* While philips SA2400 drive the PA bias from 1008c2ecf20Sopenharmony_ci * sa2400, for MAXIM we do this directly from BB */ 1018c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 3, txpw); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci max2820_write_phy_antenna(dev, channel); 1048c2ecf20Sopenharmony_ci write_max2820(dev, 3, chan); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void max2820_rf_stop(struct ieee80211_hw *dev) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 3, 0x8); 1108c2ecf20Sopenharmony_ci write_max2820(dev, 1, 0); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void max2820_rf_init(struct ieee80211_hw *dev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct rtl8180_priv *priv = dev->priv; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* MAXIM from netbsd driver */ 1198c2ecf20Sopenharmony_ci write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */ 1208c2ecf20Sopenharmony_ci write_max2820(dev, 1, 0x01e); /* enable register */ 1218c2ecf20Sopenharmony_ci write_max2820(dev, 2, 0x001); /* synt register */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci max2820_rf_set_channel(dev, NULL); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci write_max2820(dev, 4, 0x313); /* rx register */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* PA is driven directly by the BB, we keep the MAXIM bias 1288c2ecf20Sopenharmony_ci * at the highest value in case that setting it to lower 1298c2ecf20Sopenharmony_ci * values may introduce some further attenuation somewhere.. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci write_max2820(dev, 5, 0x00f); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* baseband configuration */ 1348c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0, 0x88); /* sys1 */ 1358c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 3, 0x08); /* txagc */ 1368c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 4, 0xf8); /* lnadet */ 1378c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 5, 0x90); /* ifagcinit */ 1388c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 6, 0x1a); /* ifagclimit */ 1398c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 7, 0x64); /* ifagcdet */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci max2820_write_phy_antenna(dev, 1); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x11, 0x88); /* trl */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & 1468c2ecf20Sopenharmony_ci RTL818X_CONFIG2_ANTENNA_DIV) 1478c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x12, 0xc7); 1488c2ecf20Sopenharmony_ci else 1498c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x12, 0x47); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x13, 0x9b); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x19, 0x0); /* CHESTLIM */ 1548c2ecf20Sopenharmony_ci rtl8180_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM */ 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci max2820_rf_set_channel(dev, NULL); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciconst struct rtl818x_rf_ops max2820_rf_ops = { 1608c2ecf20Sopenharmony_ci .name = "Maxim", 1618c2ecf20Sopenharmony_ci .init = max2820_rf_init, 1628c2ecf20Sopenharmony_ci .stop = max2820_rf_stop, 1638c2ecf20Sopenharmony_ci .set_chan = max2820_rf_set_channel, 1648c2ecf20Sopenharmony_ci .calc_rssi = max2820_rf_calc_rssi, 1658c2ecf20Sopenharmony_ci}; 166