18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2003, Jouni Malinen <j@w1.fi> 78c2ecf20Sopenharmony_ci * Copyright (c) 2004-2007, Michael Wu <flamingice@sourmilk.net> 88c2ecf20Sopenharmony_ci * Some parts copyright (c) 2003 by David Young <dyoung@pobox.com> 98c2ecf20Sopenharmony_ci * and used with permission. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Much thanks to Infineon-ADMtek for their support of this driver. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/if.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/crc32.h> 228c2ecf20Sopenharmony_ci#include <linux/eeprom_93cx6.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <net/mac80211.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "adm8211.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); 298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen <j@w1.fi>"); 308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for IEEE 802.11b wireless cards based on ADMtek ADM8211"); 318c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("ADM8211"); 328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic unsigned int tx_ring_size __read_mostly = 16; 358c2ecf20Sopenharmony_cistatic unsigned int rx_ring_size __read_mostly = 16; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cimodule_param(tx_ring_size, uint, 0); 388c2ecf20Sopenharmony_cimodule_param(rx_ring_size, uint, 0); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const struct pci_device_id adm8211_pci_id_table[] = { 418c2ecf20Sopenharmony_ci /* ADMtek ADM8211 */ 428c2ecf20Sopenharmony_ci { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */ 438c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1200, 0x8201) }, /* ? */ 448c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */ 458c2ecf20Sopenharmony_ci { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */ 468c2ecf20Sopenharmony_ci { 0 } 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct ieee80211_rate adm8211_rates[] = { 508c2ecf20Sopenharmony_ci { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 518c2ecf20Sopenharmony_ci { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 528c2ecf20Sopenharmony_ci { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 538c2ecf20Sopenharmony_ci { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 548c2ecf20Sopenharmony_ci { .bitrate = 220, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, /* XX ?? */ 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct ieee80211_channel adm8211_channels[] = { 588c2ecf20Sopenharmony_ci { .center_freq = 2412}, 598c2ecf20Sopenharmony_ci { .center_freq = 2417}, 608c2ecf20Sopenharmony_ci { .center_freq = 2422}, 618c2ecf20Sopenharmony_ci { .center_freq = 2427}, 628c2ecf20Sopenharmony_ci { .center_freq = 2432}, 638c2ecf20Sopenharmony_ci { .center_freq = 2437}, 648c2ecf20Sopenharmony_ci { .center_freq = 2442}, 658c2ecf20Sopenharmony_ci { .center_freq = 2447}, 668c2ecf20Sopenharmony_ci { .center_freq = 2452}, 678c2ecf20Sopenharmony_ci { .center_freq = 2457}, 688c2ecf20Sopenharmony_ci { .center_freq = 2462}, 698c2ecf20Sopenharmony_ci { .center_freq = 2467}, 708c2ecf20Sopenharmony_ci { .center_freq = 2472}, 718c2ecf20Sopenharmony_ci { .center_freq = 2484}, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void adm8211_eeprom_register_read(struct eeprom_93cx6 *eeprom) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct adm8211_priv *priv = eeprom->data; 788c2ecf20Sopenharmony_ci u32 reg = ADM8211_CSR_READ(SPR); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci eeprom->reg_data_in = reg & ADM8211_SPR_SDI; 818c2ecf20Sopenharmony_ci eeprom->reg_data_out = reg & ADM8211_SPR_SDO; 828c2ecf20Sopenharmony_ci eeprom->reg_data_clock = reg & ADM8211_SPR_SCLK; 838c2ecf20Sopenharmony_ci eeprom->reg_chip_select = reg & ADM8211_SPR_SCS; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void adm8211_eeprom_register_write(struct eeprom_93cx6 *eeprom) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct adm8211_priv *priv = eeprom->data; 898c2ecf20Sopenharmony_ci u32 reg = 0x4000 | ADM8211_SPR_SRS; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (eeprom->reg_data_in) 928c2ecf20Sopenharmony_ci reg |= ADM8211_SPR_SDI; 938c2ecf20Sopenharmony_ci if (eeprom->reg_data_out) 948c2ecf20Sopenharmony_ci reg |= ADM8211_SPR_SDO; 958c2ecf20Sopenharmony_ci if (eeprom->reg_data_clock) 968c2ecf20Sopenharmony_ci reg |= ADM8211_SPR_SCLK; 978c2ecf20Sopenharmony_ci if (eeprom->reg_chip_select) 988c2ecf20Sopenharmony_ci reg |= ADM8211_SPR_SCS; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SPR, reg); 1018c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SPR); /* eeprom_delay */ 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int adm8211_read_eeprom(struct ieee80211_hw *dev) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 1078c2ecf20Sopenharmony_ci unsigned int words, i; 1088c2ecf20Sopenharmony_ci struct ieee80211_chan_range chan_range; 1098c2ecf20Sopenharmony_ci u16 cr49; 1108c2ecf20Sopenharmony_ci struct eeprom_93cx6 eeprom = { 1118c2ecf20Sopenharmony_ci .data = priv, 1128c2ecf20Sopenharmony_ci .register_read = adm8211_eeprom_register_read, 1138c2ecf20Sopenharmony_ci .register_write = adm8211_eeprom_register_write 1148c2ecf20Sopenharmony_ci }; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (ADM8211_CSR_READ(CSR_TEST0) & ADM8211_CSR_TEST0_EPTYP) { 1178c2ecf20Sopenharmony_ci /* 256 * 16-bit = 512 bytes */ 1188c2ecf20Sopenharmony_ci eeprom.width = PCI_EEPROM_WIDTH_93C66; 1198c2ecf20Sopenharmony_ci words = 256; 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci /* 64 * 16-bit = 128 bytes */ 1228c2ecf20Sopenharmony_ci eeprom.width = PCI_EEPROM_WIDTH_93C46; 1238c2ecf20Sopenharmony_ci words = 64; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci priv->eeprom_len = words * 2; 1278c2ecf20Sopenharmony_ci priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL); 1288c2ecf20Sopenharmony_ci if (!priv->eeprom) 1298c2ecf20Sopenharmony_ci return -ENOMEM; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci eeprom_93cx6_multiread(&eeprom, 0, (__le16 *)priv->eeprom, words); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci cr49 = le16_to_cpu(priv->eeprom->cr49); 1348c2ecf20Sopenharmony_ci priv->rf_type = (cr49 >> 3) & 0x7; 1358c2ecf20Sopenharmony_ci switch (priv->rf_type) { 1368c2ecf20Sopenharmony_ci case ADM8211_TYPE_INTERSIL: 1378c2ecf20Sopenharmony_ci case ADM8211_TYPE_RFMD: 1388c2ecf20Sopenharmony_ci case ADM8211_TYPE_MARVEL: 1398c2ecf20Sopenharmony_ci case ADM8211_TYPE_AIROHA: 1408c2ecf20Sopenharmony_ci case ADM8211_TYPE_ADMTEK: 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci default: 1448c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_CA) 1458c2ecf20Sopenharmony_ci priv->rf_type = ADM8211_TYPE_RFMD; 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci priv->rf_type = ADM8211_TYPE_AIROHA; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s (adm8211): Unknown RFtype %d\n", 1508c2ecf20Sopenharmony_ci pci_name(priv->pdev), (cr49 >> 3) & 0x7); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci priv->bbp_type = cr49 & 0x7; 1548c2ecf20Sopenharmony_ci switch (priv->bbp_type) { 1558c2ecf20Sopenharmony_ci case ADM8211_TYPE_INTERSIL: 1568c2ecf20Sopenharmony_ci case ADM8211_TYPE_RFMD: 1578c2ecf20Sopenharmony_ci case ADM8211_TYPE_MARVEL: 1588c2ecf20Sopenharmony_ci case ADM8211_TYPE_AIROHA: 1598c2ecf20Sopenharmony_ci case ADM8211_TYPE_ADMTEK: 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci default: 1628c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_CA) 1638c2ecf20Sopenharmony_ci priv->bbp_type = ADM8211_TYPE_RFMD; 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci priv->bbp_type = ADM8211_TYPE_ADMTEK; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s (adm8211): Unknown BBPtype: %d\n", 1688c2ecf20Sopenharmony_ci pci_name(priv->pdev), cr49 >> 3); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) { 1728c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s (adm8211): Invalid country code (%d)\n", 1738c2ecf20Sopenharmony_ci pci_name(priv->pdev), priv->eeprom->country_code); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci chan_range = cranges[2]; 1768c2ecf20Sopenharmony_ci } else 1778c2ecf20Sopenharmony_ci chan_range = cranges[priv->eeprom->country_code]; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n", 1808c2ecf20Sopenharmony_ci pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(priv->channels) != sizeof(adm8211_channels)); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci memcpy(priv->channels, adm8211_channels, sizeof(priv->channels)); 1858c2ecf20Sopenharmony_ci priv->band.channels = priv->channels; 1868c2ecf20Sopenharmony_ci priv->band.n_channels = ARRAY_SIZE(adm8211_channels); 1878c2ecf20Sopenharmony_ci priv->band.bitrates = adm8211_rates; 1888c2ecf20Sopenharmony_ci priv->band.n_bitrates = ARRAY_SIZE(adm8211_rates); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 1; i <= ARRAY_SIZE(adm8211_channels); i++) 1918c2ecf20Sopenharmony_ci if (i < chan_range.min || i > chan_range.max) 1928c2ecf20Sopenharmony_ci priv->channels[i - 1].flags |= IEEE80211_CHAN_DISABLED; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (priv->eeprom->specific_bbptype) { 1958c2ecf20Sopenharmony_ci case ADM8211_BBP_RFMD3000: 1968c2ecf20Sopenharmony_ci case ADM8211_BBP_RFMD3002: 1978c2ecf20Sopenharmony_ci case ADM8211_BBP_ADM8011: 1988c2ecf20Sopenharmony_ci priv->specific_bbptype = priv->eeprom->specific_bbptype; 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci default: 2028c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_CA) 2038c2ecf20Sopenharmony_ci priv->specific_bbptype = ADM8211_BBP_RFMD3000; 2048c2ecf20Sopenharmony_ci else 2058c2ecf20Sopenharmony_ci priv->specific_bbptype = ADM8211_BBP_ADM8011; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s (adm8211): Unknown specific BBP: %d\n", 2088c2ecf20Sopenharmony_ci pci_name(priv->pdev), priv->eeprom->specific_bbptype); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci switch (priv->eeprom->specific_rftype) { 2128c2ecf20Sopenharmony_ci case ADM8211_RFMD2948: 2138c2ecf20Sopenharmony_ci case ADM8211_RFMD2958: 2148c2ecf20Sopenharmony_ci case ADM8211_RFMD2958_RF3000_CONTROL_POWER: 2158c2ecf20Sopenharmony_ci case ADM8211_MAX2820: 2168c2ecf20Sopenharmony_ci case ADM8211_AL2210L: 2178c2ecf20Sopenharmony_ci priv->transceiver_type = priv->eeprom->specific_rftype; 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci default: 2218c2ecf20Sopenharmony_ci if (priv->pdev->revision == ADM8211_REV_BA) 2228c2ecf20Sopenharmony_ci priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER; 2238c2ecf20Sopenharmony_ci else if (priv->pdev->revision == ADM8211_REV_CA) 2248c2ecf20Sopenharmony_ci priv->transceiver_type = ADM8211_AL2210L; 2258c2ecf20Sopenharmony_ci else if (priv->pdev->revision == ADM8211_REV_AB) 2268c2ecf20Sopenharmony_ci priv->transceiver_type = ADM8211_RFMD2948; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s (adm8211): Unknown transceiver: %d\n", 2298c2ecf20Sopenharmony_ci pci_name(priv->pdev), priv->eeprom->specific_rftype); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d " 2358c2ecf20Sopenharmony_ci "Transceiver=%d\n", pci_name(priv->pdev), priv->rf_type, 2368c2ecf20Sopenharmony_ci priv->bbp_type, priv->specific_bbptype, priv->transceiver_type); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic inline void adm8211_write_sram(struct ieee80211_hw *dev, 2428c2ecf20Sopenharmony_ci u32 addr, u32 data) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(WEPCTL, addr | ADM8211_WEPCTL_TABLE_WR | 2478c2ecf20Sopenharmony_ci (priv->pdev->revision < ADM8211_REV_BA ? 2488c2ecf20Sopenharmony_ci 0 : ADM8211_WEPCTL_SEL_WEPTABLE )); 2498c2ecf20Sopenharmony_ci ADM8211_CSR_READ(WEPCTL); 2508c2ecf20Sopenharmony_ci msleep(1); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(WESK, data); 2538c2ecf20Sopenharmony_ci ADM8211_CSR_READ(WESK); 2548c2ecf20Sopenharmony_ci msleep(1); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void adm8211_write_sram_bytes(struct ieee80211_hw *dev, 2588c2ecf20Sopenharmony_ci unsigned int addr, u8 *buf, 2598c2ecf20Sopenharmony_ci unsigned int len) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 2628c2ecf20Sopenharmony_ci u32 reg = ADM8211_CSR_READ(WEPCTL); 2638c2ecf20Sopenharmony_ci unsigned int i; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_BA) { 2668c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 2) { 2678c2ecf20Sopenharmony_ci u16 val = buf[i] | (buf[i + 1] << 8); 2688c2ecf20Sopenharmony_ci adm8211_write_sram(dev, addr + i / 2, val); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } else { 2718c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 4) { 2728c2ecf20Sopenharmony_ci u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) | 2738c2ecf20Sopenharmony_ci (buf[i + 2] << 16) | (buf[i + 3] << 24); 2748c2ecf20Sopenharmony_ci adm8211_write_sram(dev, addr + i / 4, val); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(WEPCTL, reg); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void adm8211_clear_sram(struct ieee80211_hw *dev) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 2848c2ecf20Sopenharmony_ci u32 reg = ADM8211_CSR_READ(WEPCTL); 2858c2ecf20Sopenharmony_ci unsigned int addr; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++) 2888c2ecf20Sopenharmony_ci adm8211_write_sram(dev, addr, 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(WEPCTL, reg); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int adm8211_get_stats(struct ieee80211_hw *dev, 2948c2ecf20Sopenharmony_ci struct ieee80211_low_level_stats *stats) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci memcpy(stats, &priv->stats, sizeof(*stats)); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void adm8211_interrupt_tci(struct ieee80211_hw *dev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 3068c2ecf20Sopenharmony_ci unsigned int dirty_tx; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock(&priv->lock); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci for (dirty_tx = priv->dirty_tx; priv->cur_tx - dirty_tx; dirty_tx++) { 3118c2ecf20Sopenharmony_ci unsigned int entry = dirty_tx % priv->tx_ring_size; 3128c2ecf20Sopenharmony_ci u32 status = le32_to_cpu(priv->tx_ring[entry].status); 3138c2ecf20Sopenharmony_ci struct ieee80211_tx_info *txi; 3148c2ecf20Sopenharmony_ci struct adm8211_tx_ring_info *info; 3158c2ecf20Sopenharmony_ci struct sk_buff *skb; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (status & TDES0_CONTROL_OWN || 3188c2ecf20Sopenharmony_ci !(status & TDES0_CONTROL_DONE)) 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci info = &priv->tx_buffers[entry]; 3228c2ecf20Sopenharmony_ci skb = info->skb; 3238c2ecf20Sopenharmony_ci txi = IEEE80211_SKB_CB(skb); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */ 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, info->mapping, 3288c2ecf20Sopenharmony_ci info->skb->len, DMA_TO_DEVICE); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(txi); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct adm8211_tx_hdr)); 3338c2ecf20Sopenharmony_ci memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen); 3348c2ecf20Sopenharmony_ci if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && 3358c2ecf20Sopenharmony_ci !(status & TDES0_STATUS_ES)) 3368c2ecf20Sopenharmony_ci txi->flags |= IEEE80211_TX_STAT_ACK; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(dev, skb); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci info->skb = NULL; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2) 3448c2ecf20Sopenharmony_ci ieee80211_wake_queue(dev, 0); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci priv->dirty_tx = dirty_tx; 3478c2ecf20Sopenharmony_ci spin_unlock(&priv->lock); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void adm8211_interrupt_rci(struct ieee80211_hw *dev) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 3548c2ecf20Sopenharmony_ci unsigned int entry = priv->cur_rx % priv->rx_ring_size; 3558c2ecf20Sopenharmony_ci u32 status; 3568c2ecf20Sopenharmony_ci unsigned int pktlen; 3578c2ecf20Sopenharmony_ci struct sk_buff *skb, *newskb; 3588c2ecf20Sopenharmony_ci unsigned int limit = priv->rx_ring_size; 3598c2ecf20Sopenharmony_ci u8 rssi, rate; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci while (!(priv->rx_ring[entry].status & cpu_to_le32(RDES0_STATUS_OWN))) { 3628c2ecf20Sopenharmony_ci if (!limit--) 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci status = le32_to_cpu(priv->rx_ring[entry].status); 3668c2ecf20Sopenharmony_ci rate = (status & RDES0_STATUS_RXDR) >> 12; 3678c2ecf20Sopenharmony_ci rssi = le32_to_cpu(priv->rx_ring[entry].length) & 3688c2ecf20Sopenharmony_ci RDES1_STATUS_RSSI; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci pktlen = status & RDES0_STATUS_FL; 3718c2ecf20Sopenharmony_ci if (pktlen > RX_PKT_SIZE) { 3728c2ecf20Sopenharmony_ci if (net_ratelimit()) 3738c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, "frame too long (%d)\n", 3748c2ecf20Sopenharmony_ci pktlen); 3758c2ecf20Sopenharmony_ci pktlen = RX_PKT_SIZE; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) { 3798c2ecf20Sopenharmony_ci skb = NULL; /* old buffer will be reused */ 3808c2ecf20Sopenharmony_ci /* TODO: update RX error stats */ 3818c2ecf20Sopenharmony_ci /* TODO: check RDES0_STATUS_CRC*E */ 3828c2ecf20Sopenharmony_ci } else if (pktlen < RX_COPY_BREAK) { 3838c2ecf20Sopenharmony_ci skb = dev_alloc_skb(pktlen); 3848c2ecf20Sopenharmony_ci if (skb) { 3858c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&priv->pdev->dev, 3868c2ecf20Sopenharmony_ci priv->rx_buffers[entry].mapping, 3878c2ecf20Sopenharmony_ci pktlen, 3888c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3898c2ecf20Sopenharmony_ci skb_put_data(skb, 3908c2ecf20Sopenharmony_ci skb_tail_pointer(priv->rx_buffers[entry].skb), 3918c2ecf20Sopenharmony_ci pktlen); 3928c2ecf20Sopenharmony_ci dma_sync_single_for_device(&priv->pdev->dev, 3938c2ecf20Sopenharmony_ci priv->rx_buffers[entry].mapping, 3948c2ecf20Sopenharmony_ci RX_PKT_SIZE, 3958c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci newskb = dev_alloc_skb(RX_PKT_SIZE); 3998c2ecf20Sopenharmony_ci if (newskb) { 4008c2ecf20Sopenharmony_ci skb = priv->rx_buffers[entry].skb; 4018c2ecf20Sopenharmony_ci skb_put(skb, pktlen); 4028c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 4038c2ecf20Sopenharmony_ci priv->rx_buffers[entry].mapping, 4048c2ecf20Sopenharmony_ci RX_PKT_SIZE, DMA_FROM_DEVICE); 4058c2ecf20Sopenharmony_ci priv->rx_buffers[entry].skb = newskb; 4068c2ecf20Sopenharmony_ci priv->rx_buffers[entry].mapping = 4078c2ecf20Sopenharmony_ci dma_map_single(&priv->pdev->dev, 4088c2ecf20Sopenharmony_ci skb_tail_pointer(newskb), 4098c2ecf20Sopenharmony_ci RX_PKT_SIZE, 4108c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4118c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, 4128c2ecf20Sopenharmony_ci priv->rx_buffers[entry].mapping)) { 4138c2ecf20Sopenharmony_ci priv->rx_buffers[entry].skb = NULL; 4148c2ecf20Sopenharmony_ci dev_kfree_skb(newskb); 4158c2ecf20Sopenharmony_ci skb = NULL; 4168c2ecf20Sopenharmony_ci /* TODO: update rx dropped stats */ 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci skb = NULL; 4208c2ecf20Sopenharmony_ci /* TODO: update rx dropped stats */ 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci priv->rx_ring[entry].buffer1 = 4248c2ecf20Sopenharmony_ci cpu_to_le32(priv->rx_buffers[entry].mapping); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci priv->rx_ring[entry].status = cpu_to_le32(RDES0_STATUS_OWN | 4288c2ecf20Sopenharmony_ci RDES0_STATUS_SQL); 4298c2ecf20Sopenharmony_ci priv->rx_ring[entry].length = 4308c2ecf20Sopenharmony_ci cpu_to_le32(RX_PKT_SIZE | 4318c2ecf20Sopenharmony_ci (entry == priv->rx_ring_size - 1 ? 4328c2ecf20Sopenharmony_ci RDES1_CONTROL_RER : 0)); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (skb) { 4358c2ecf20Sopenharmony_ci struct ieee80211_rx_status rx_status = {0}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_CA) 4388c2ecf20Sopenharmony_ci rx_status.signal = rssi; 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci rx_status.signal = 100 - rssi; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci rx_status.rate_idx = rate; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci rx_status.freq = adm8211_channels[priv->channel - 1].center_freq; 4458c2ecf20Sopenharmony_ci rx_status.band = NL80211_BAND_2GHZ; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); 4488c2ecf20Sopenharmony_ci ieee80211_rx_irqsafe(dev, skb); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci entry = (++priv->cur_rx) % priv->rx_ring_size; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* TODO: check LPC and update stats? */ 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic irqreturn_t adm8211_interrupt(int irq, void *dev_id) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci#define ADM8211_INT(x) \ 4618c2ecf20Sopenharmony_cido { \ 4628c2ecf20Sopenharmony_ci if (unlikely(stsr & ADM8211_STSR_ ## x)) \ 4638c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, "%s\n", #x); \ 4648c2ecf20Sopenharmony_ci} while (0) 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = dev_id; 4678c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 4688c2ecf20Sopenharmony_ci u32 stsr = ADM8211_CSR_READ(STSR); 4698c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(STSR, stsr); 4708c2ecf20Sopenharmony_ci if (stsr == 0xffffffff) 4718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS))) 4748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (stsr & ADM8211_STSR_RCI) 4778c2ecf20Sopenharmony_ci adm8211_interrupt_rci(dev); 4788c2ecf20Sopenharmony_ci if (stsr & ADM8211_STSR_TCI) 4798c2ecf20Sopenharmony_ci adm8211_interrupt_tci(dev); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci ADM8211_INT(PCF); 4828c2ecf20Sopenharmony_ci ADM8211_INT(BCNTC); 4838c2ecf20Sopenharmony_ci ADM8211_INT(GPINT); 4848c2ecf20Sopenharmony_ci ADM8211_INT(ATIMTC); 4858c2ecf20Sopenharmony_ci ADM8211_INT(TSFTF); 4868c2ecf20Sopenharmony_ci ADM8211_INT(TSCZ); 4878c2ecf20Sopenharmony_ci ADM8211_INT(SQL); 4888c2ecf20Sopenharmony_ci ADM8211_INT(WEPTD); 4898c2ecf20Sopenharmony_ci ADM8211_INT(ATIME); 4908c2ecf20Sopenharmony_ci ADM8211_INT(TEIS); 4918c2ecf20Sopenharmony_ci ADM8211_INT(FBE); 4928c2ecf20Sopenharmony_ci ADM8211_INT(REIS); 4938c2ecf20Sopenharmony_ci ADM8211_INT(GPTT); 4948c2ecf20Sopenharmony_ci ADM8211_INT(RPS); 4958c2ecf20Sopenharmony_ci ADM8211_INT(RDU); 4968c2ecf20Sopenharmony_ci ADM8211_INT(TUF); 4978c2ecf20Sopenharmony_ci ADM8211_INT(TPS); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci#undef ADM8211_INT 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci#define WRITE_SYN(name,v_mask,v_shift,a_mask,a_shift,bits,prewrite,postwrite)\ 5058c2ecf20Sopenharmony_cistatic void adm8211_rf_write_syn_ ## name (struct ieee80211_hw *dev, \ 5068c2ecf20Sopenharmony_ci u16 addr, u32 value) { \ 5078c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; \ 5088c2ecf20Sopenharmony_ci unsigned int i; \ 5098c2ecf20Sopenharmony_ci u32 reg, bitbuf; \ 5108c2ecf20Sopenharmony_ci \ 5118c2ecf20Sopenharmony_ci value &= v_mask; \ 5128c2ecf20Sopenharmony_ci addr &= a_mask; \ 5138c2ecf20Sopenharmony_ci bitbuf = (value << v_shift) | (addr << a_shift); \ 5148c2ecf20Sopenharmony_ci \ 5158c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1); \ 5168c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5178c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0); \ 5188c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5198c2ecf20Sopenharmony_ci \ 5208c2ecf20Sopenharmony_ci if (prewrite) { \ 5218c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0); \ 5228c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5238c2ecf20Sopenharmony_ci } \ 5248c2ecf20Sopenharmony_ci \ 5258c2ecf20Sopenharmony_ci for (i = 0; i <= bits; i++) { \ 5268c2ecf20Sopenharmony_ci if (bitbuf & (1 << (bits - i))) \ 5278c2ecf20Sopenharmony_ci reg = ADM8211_SYNRF_WRITE_SYNDATA_1; \ 5288c2ecf20Sopenharmony_ci else \ 5298c2ecf20Sopenharmony_ci reg = ADM8211_SYNRF_WRITE_SYNDATA_0; \ 5308c2ecf20Sopenharmony_ci \ 5318c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, reg); \ 5328c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5338c2ecf20Sopenharmony_ci \ 5348c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1); \ 5358c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5368c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0); \ 5378c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5388c2ecf20Sopenharmony_ci } \ 5398c2ecf20Sopenharmony_ci \ 5408c2ecf20Sopenharmony_ci if (postwrite == 1) { \ 5418c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0); \ 5428c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5438c2ecf20Sopenharmony_ci } \ 5448c2ecf20Sopenharmony_ci if (postwrite == 2) { \ 5458c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1); \ 5468c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5478c2ecf20Sopenharmony_ci } \ 5488c2ecf20Sopenharmony_ci \ 5498c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, 0); \ 5508c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); \ 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciWRITE_SYN(max2820, 0x00FFF, 0, 0x0F, 12, 15, 1, 1) 5548c2ecf20Sopenharmony_ciWRITE_SYN(al2210l, 0xFFFFF, 4, 0x0F, 0, 23, 1, 1) 5558c2ecf20Sopenharmony_ciWRITE_SYN(rfmd2958, 0x3FFFF, 0, 0x1F, 18, 23, 0, 1) 5568c2ecf20Sopenharmony_ciWRITE_SYN(rfmd2948, 0x0FFFF, 4, 0x0F, 0, 21, 0, 2) 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci#undef WRITE_SYN 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int adm8211_write_bbp(struct ieee80211_hw *dev, u8 addr, u8 data) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 5638c2ecf20Sopenharmony_ci unsigned int timeout; 5648c2ecf20Sopenharmony_ci u32 reg; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci timeout = 10; 5678c2ecf20Sopenharmony_ci while (timeout > 0) { 5688c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(BBPCTL); 5698c2ecf20Sopenharmony_ci if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD))) 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci timeout--; 5728c2ecf20Sopenharmony_ci msleep(2); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (timeout == 0) { 5768c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, 5778c2ecf20Sopenharmony_ci "adm8211_write_bbp(%d,%d) failed prewrite (reg=0x%08x)\n", 5788c2ecf20Sopenharmony_ci addr, data, reg); 5798c2ecf20Sopenharmony_ci return -ETIMEDOUT; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci switch (priv->bbp_type) { 5838c2ecf20Sopenharmony_ci case ADM8211_TYPE_INTERSIL: 5848c2ecf20Sopenharmony_ci reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */ 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci case ADM8211_TYPE_RFMD: 5878c2ecf20Sopenharmony_ci reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | 5888c2ecf20Sopenharmony_ci (0x01 << 18); 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci case ADM8211_TYPE_ADMTEK: 5918c2ecf20Sopenharmony_ci reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | 5928c2ecf20Sopenharmony_ci (0x05 << 18); 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci reg |= ADM8211_BBPCTL_WR | (addr << 8) | data; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(BBPCTL, reg); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci timeout = 10; 6008c2ecf20Sopenharmony_ci while (timeout > 0) { 6018c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(BBPCTL); 6028c2ecf20Sopenharmony_ci if (!(reg & ADM8211_BBPCTL_WR)) 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci timeout--; 6058c2ecf20Sopenharmony_ci msleep(2); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (timeout == 0) { 6098c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) & 6108c2ecf20Sopenharmony_ci ~ADM8211_BBPCTL_WR); 6118c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, 6128c2ecf20Sopenharmony_ci "adm8211_write_bbp(%d,%d) failed postwrite (reg=0x%08x)\n", 6138c2ecf20Sopenharmony_ci addr, data, reg); 6148c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int adm8211_rf_set_channel(struct ieee80211_hw *dev, unsigned int chan) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci static const u32 adm8211_rfmd2958_reg5[] = 6238c2ecf20Sopenharmony_ci {0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340, 6248c2ecf20Sopenharmony_ci 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7}; 6258c2ecf20Sopenharmony_ci static const u32 adm8211_rfmd2958_reg6[] = 6268c2ecf20Sopenharmony_ci {0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000, 6278c2ecf20Sopenharmony_ci 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745}; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 6308c2ecf20Sopenharmony_ci u8 ant_power = priv->ant_power > 0x3F ? 6318c2ecf20Sopenharmony_ci priv->eeprom->antenna_power[chan - 1] : priv->ant_power; 6328c2ecf20Sopenharmony_ci u8 tx_power = priv->tx_power > 0x3F ? 6338c2ecf20Sopenharmony_ci priv->eeprom->tx_power[chan - 1] : priv->tx_power; 6348c2ecf20Sopenharmony_ci u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ? 6358c2ecf20Sopenharmony_ci priv->eeprom->lpf_cutoff[chan - 1] : priv->lpf_cutoff; 6368c2ecf20Sopenharmony_ci u8 lnags_thresh = priv->lnags_threshold == 0xFF ? 6378c2ecf20Sopenharmony_ci priv->eeprom->lnags_threshold[chan - 1] : priv->lnags_threshold; 6388c2ecf20Sopenharmony_ci u32 reg; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci ADM8211_IDLE(); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* Program synthesizer to new channel */ 6438c2ecf20Sopenharmony_ci switch (priv->transceiver_type) { 6448c2ecf20Sopenharmony_ci case ADM8211_RFMD2958: 6458c2ecf20Sopenharmony_ci case ADM8211_RFMD2958_RF3000_CONTROL_POWER: 6468c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007); 6478c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x05, 6508c2ecf20Sopenharmony_ci adm8211_rfmd2958_reg5[chan - 1]); 6518c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x06, 6528c2ecf20Sopenharmony_ci adm8211_rfmd2958_reg6[chan - 1]); 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci case ADM8211_RFMD2948: 6568c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, 6578c2ecf20Sopenharmony_ci SI4126_MAIN_XINDIV2); 6588c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN, 6598c2ecf20Sopenharmony_ci SI4126_POWERDOWN_PDIB | 6608c2ecf20Sopenharmony_ci SI4126_POWERDOWN_PDRB); 6618c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0); 6628c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV, 6638c2ecf20Sopenharmony_ci (chan == 14 ? 6648c2ecf20Sopenharmony_ci 2110 : (2033 + (chan * 5)))); 6658c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496); 6668c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44); 6678c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44); 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci case ADM8211_MAX2820: 6718c2ecf20Sopenharmony_ci adm8211_rf_write_syn_max2820(dev, 0x3, 6728c2ecf20Sopenharmony_ci (chan == 14 ? 0x054 : (0x7 + (chan * 5)))); 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci case ADM8211_AL2210L: 6768c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x0, 6778c2ecf20Sopenharmony_ci (chan == 14 ? 0x229B4 : (0x22967 + (chan * 5)))); 6788c2ecf20Sopenharmony_ci break; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci default: 6818c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, "unsupported transceiver type %d\n", 6828c2ecf20Sopenharmony_ci priv->transceiver_type); 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* write BBP regs */ 6878c2ecf20Sopenharmony_ci if (priv->bbp_type == ADM8211_TYPE_RFMD) { 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */ 6908c2ecf20Sopenharmony_ci /* TODO: remove if SMC 2635W doesn't need this */ 6918c2ecf20Sopenharmony_ci if (priv->transceiver_type == ADM8211_RFMD2948) { 6928c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(GPIO); 6938c2ecf20Sopenharmony_ci reg &= 0xfffc0000; 6948c2ecf20Sopenharmony_ci reg |= ADM8211_CSR_GPIO_EN0; 6958c2ecf20Sopenharmony_ci if (chan != 14) 6968c2ecf20Sopenharmony_ci reg |= ADM8211_CSR_GPIO_O0; 6978c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(GPIO, reg); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (priv->transceiver_type == ADM8211_RFMD2958) { 7018c2ecf20Sopenharmony_ci /* set PCNT2 */ 7028c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100); 7038c2ecf20Sopenharmony_ci /* set PCNT1 P_DESIRED/MID_BIAS */ 7048c2ecf20Sopenharmony_ci reg = le16_to_cpu(priv->eeprom->cr49); 7058c2ecf20Sopenharmony_ci reg >>= 13; 7068c2ecf20Sopenharmony_ci reg <<= 15; 7078c2ecf20Sopenharmony_ci reg |= ant_power << 9; 7088c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg); 7098c2ecf20Sopenharmony_ci /* set TXRX TX_GAIN */ 7108c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 | 7118c2ecf20Sopenharmony_ci (priv->pdev->revision < ADM8211_REV_CA ? tx_power : 0)); 7128c2ecf20Sopenharmony_ci } else { 7138c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(PLCPHD); 7148c2ecf20Sopenharmony_ci reg &= 0xff00ffff; 7158c2ecf20Sopenharmony_ci reg |= tx_power << 18; 7168c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PLCPHD, reg); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | 7208c2ecf20Sopenharmony_ci ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); 7218c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); 7228c2ecf20Sopenharmony_ci msleep(30); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* RF3000 BBP */ 7258c2ecf20Sopenharmony_ci if (priv->transceiver_type != ADM8211_RFMD2958) 7268c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 7278c2ecf20Sopenharmony_ci tx_power<<2); 7288c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff); 7298c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh); 7308c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1c, priv->pdev->revision == ADM8211_REV_BA ? 7318c2ecf20Sopenharmony_ci priv->eeprom->cr28 : 0); 7328c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, 0); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* Nothing to do for ADMtek BBP */ 7378c2ecf20Sopenharmony_ci } else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) 7388c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, "unsupported BBP type %d\n", 7398c2ecf20Sopenharmony_ci priv->bbp_type); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci ADM8211_RESTORE(); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* update current channel for adhoc (and maybe AP mode) */ 7448c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CAP0); 7458c2ecf20Sopenharmony_ci reg &= ~0xF; 7468c2ecf20Sopenharmony_ci reg |= chan; 7478c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CAP0, reg); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return 0; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic void adm8211_update_mode(struct ieee80211_hw *dev) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci ADM8211_IDLE(); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci priv->soft_rx_crc = 0; 7598c2ecf20Sopenharmony_ci switch (priv->mode) { 7608c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 7618c2ecf20Sopenharmony_ci priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA); 7628c2ecf20Sopenharmony_ci priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR; 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 7658c2ecf20Sopenharmony_ci priv->nar &= ~ADM8211_NAR_PR; 7668c2ecf20Sopenharmony_ci priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* don't trust the error bits on rev 0x20 and up in adhoc */ 7698c2ecf20Sopenharmony_ci if (priv->pdev->revision >= ADM8211_REV_BA) 7708c2ecf20Sopenharmony_ci priv->soft_rx_crc = 1; 7718c2ecf20Sopenharmony_ci break; 7728c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 7738c2ecf20Sopenharmony_ci priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST); 7748c2ecf20Sopenharmony_ci priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR; 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci ADM8211_RESTORE(); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void adm8211_hw_init_syn(struct ieee80211_hw *dev) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci switch (priv->transceiver_type) { 7868c2ecf20Sopenharmony_ci case ADM8211_RFMD2958: 7878c2ecf20Sopenharmony_ci case ADM8211_RFMD2958_RF3000_CONTROL_POWER: 7888c2ecf20Sopenharmony_ci /* comments taken from ADMtek vendor driver */ 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Reset RF2958 after power on */ 7918c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000); 7928c2ecf20Sopenharmony_ci /* Initialize RF VCO Core Bias to maximum */ 7938c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F); 7948c2ecf20Sopenharmony_ci /* Initialize IF PLL */ 7958c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03); 7968c2ecf20Sopenharmony_ci /* Initialize IF PLL Coarse Tuning */ 7978c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F); 7988c2ecf20Sopenharmony_ci /* Initialize RF PLL */ 7998c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403); 8008c2ecf20Sopenharmony_ci /* Initialize RF PLL Coarse Tuning */ 8018c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F); 8028c2ecf20Sopenharmony_ci /* Initialize TX gain and filter BW (R9) */ 8038c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x09, 8048c2ecf20Sopenharmony_ci (priv->transceiver_type == ADM8211_RFMD2958 ? 8058c2ecf20Sopenharmony_ci 0x10050 : 0x00050)); 8068c2ecf20Sopenharmony_ci /* Initialize CAL register */ 8078c2ecf20Sopenharmony_ci adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8); 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci case ADM8211_MAX2820: 8118c2ecf20Sopenharmony_ci adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E); 8128c2ecf20Sopenharmony_ci adm8211_rf_write_syn_max2820(dev, 0x2, 0x001); 8138c2ecf20Sopenharmony_ci adm8211_rf_write_syn_max2820(dev, 0x3, 0x054); 8148c2ecf20Sopenharmony_ci adm8211_rf_write_syn_max2820(dev, 0x4, 0x310); 8158c2ecf20Sopenharmony_ci adm8211_rf_write_syn_max2820(dev, 0x5, 0x000); 8168c2ecf20Sopenharmony_ci break; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci case ADM8211_AL2210L: 8198c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C); 8208c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB); 8218c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F); 8228c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9); 8238c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280); 8248c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641); 8258c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130); 8268c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000); 8278c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F); 8288c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C); 8298c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000); 8308c2ecf20Sopenharmony_ci adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000); 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci case ADM8211_RFMD2948: 8348c2ecf20Sopenharmony_ci default: 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic int adm8211_hw_init_bbp(struct ieee80211_hw *dev) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 8428c2ecf20Sopenharmony_ci u32 reg; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* write addresses */ 8458c2ecf20Sopenharmony_ci if (priv->bbp_type == ADM8211_TYPE_INTERSIL) { 8468c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIWA, 0x100E0C0A); 8478c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIRD0, 0x00007C7E); 8488c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIRD1, 0x00100000); 8498c2ecf20Sopenharmony_ci } else if (priv->bbp_type == ADM8211_TYPE_RFMD || 8508c2ecf20Sopenharmony_ci priv->bbp_type == ADM8211_TYPE_ADMTEK) { 8518c2ecf20Sopenharmony_ci /* check specific BBP type */ 8528c2ecf20Sopenharmony_ci switch (priv->specific_bbptype) { 8538c2ecf20Sopenharmony_ci case ADM8211_BBP_RFMD3000: 8548c2ecf20Sopenharmony_ci case ADM8211_BBP_RFMD3002: 8558c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIWA, 0x00009101); 8568c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIRD0, 0x00000301); 8578c2ecf20Sopenharmony_ci break; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci case ADM8211_BBP_ADM8011: 8608c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIWA, 0x00008903); 8618c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIRD0, 0x00001716); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(BBPCTL); 8648c2ecf20Sopenharmony_ci reg &= ~ADM8211_BBPCTL_TYPE; 8658c2ecf20Sopenharmony_ci reg |= 0x5 << 18; 8668c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(BBPCTL, reg); 8678c2ecf20Sopenharmony_ci break; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci switch (priv->pdev->revision) { 8718c2ecf20Sopenharmony_ci case ADM8211_REV_CA: 8728c2ecf20Sopenharmony_ci if (priv->transceiver_type == ADM8211_RFMD2958 || 8738c2ecf20Sopenharmony_ci priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || 8748c2ecf20Sopenharmony_ci priv->transceiver_type == ADM8211_RFMD2948) 8758c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNCTL, 0x1 << 22); 8768c2ecf20Sopenharmony_ci else if (priv->transceiver_type == ADM8211_MAX2820 || 8778c2ecf20Sopenharmony_ci priv->transceiver_type == ADM8211_AL2210L) 8788c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNCTL, 0x3 << 22); 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci case ADM8211_REV_BA: 8828c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(MMIRD1); 8838c2ecf20Sopenharmony_ci reg &= 0x0000FFFF; 8848c2ecf20Sopenharmony_ci reg |= 0x7e100000; 8858c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIRD1, reg); 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci case ADM8211_REV_AB: 8898c2ecf20Sopenharmony_ci case ADM8211_REV_AF: 8908c2ecf20Sopenharmony_ci default: 8918c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MMIRD1, 0x7e100000); 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* For RFMD */ 8968c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MACTEST, 0x800); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci adm8211_hw_init_syn(dev); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Set RF Power control IF pin to PE1+PHYRST# */ 9028c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | 9038c2ecf20Sopenharmony_ci ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); 9048c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); 9058c2ecf20Sopenharmony_ci msleep(20); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* write BBP regs */ 9088c2ecf20Sopenharmony_ci if (priv->bbp_type == ADM8211_TYPE_RFMD) { 9098c2ecf20Sopenharmony_ci /* RF3000 BBP */ 9108c2ecf20Sopenharmony_ci /* another set: 9118c2ecf20Sopenharmony_ci * 11: c8 9128c2ecf20Sopenharmony_ci * 14: 14 9138c2ecf20Sopenharmony_ci * 15: 50 (chan 1..13; chan 14: d0) 9148c2ecf20Sopenharmony_ci * 1c: 00 9158c2ecf20Sopenharmony_ci * 1d: 84 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80); 9188c2ecf20Sopenharmony_ci /* antenna selection: diversity */ 9198c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); 9208c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74); 9218c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38); 9228c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (priv->eeprom->major_version < 2) { 9258c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1c, 0x00); 9268c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1d, 0x80); 9278c2ecf20Sopenharmony_ci } else { 9288c2ecf20Sopenharmony_ci if (priv->pdev->revision == ADM8211_REV_BA) 9298c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28); 9308c2ecf20Sopenharmony_ci else 9318c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1c, 0x00); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci } else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) { 9368c2ecf20Sopenharmony_ci /* reset baseband */ 9378c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x00, 0xFF); 9388c2ecf20Sopenharmony_ci /* antenna selection: diversity */ 9398c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x07, 0x0A); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* TODO: find documentation for this */ 9428c2ecf20Sopenharmony_ci switch (priv->transceiver_type) { 9438c2ecf20Sopenharmony_ci case ADM8211_RFMD2958: 9448c2ecf20Sopenharmony_ci case ADM8211_RFMD2958_RF3000_CONTROL_POWER: 9458c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x00, 0x00); 9468c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x01, 0x00); 9478c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x02, 0x00); 9488c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x03, 0x00); 9498c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x06, 0x0f); 9508c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x09, 0x00); 9518c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0a, 0x00); 9528c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0b, 0x00); 9538c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0c, 0x00); 9548c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0f, 0xAA); 9558c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x10, 0x8c); 9568c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x11, 0x43); 9578c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x18, 0x40); 9588c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x20, 0x23); 9598c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x21, 0x02); 9608c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x22, 0x28); 9618c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x23, 0x30); 9628c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x24, 0x2d); 9638c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x28, 0x35); 9648c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2a, 0x8c); 9658c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2b, 0x81); 9668c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2c, 0x44); 9678c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2d, 0x0A); 9688c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x29, 0x40); 9698c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x60, 0x08); 9708c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x64, 0x01); 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci case ADM8211_MAX2820: 9748c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x00, 0x00); 9758c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x01, 0x00); 9768c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x02, 0x00); 9778c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x03, 0x00); 9788c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x06, 0x0f); 9798c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x09, 0x05); 9808c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0a, 0x02); 9818c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0b, 0x00); 9828c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0c, 0x0f); 9838c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0f, 0x55); 9848c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x10, 0x8d); 9858c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x11, 0x43); 9868c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x18, 0x4a); 9878c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x20, 0x20); 9888c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x21, 0x02); 9898c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x22, 0x23); 9908c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x23, 0x30); 9918c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x24, 0x2d); 9928c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2a, 0x8c); 9938c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2b, 0x81); 9948c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2c, 0x44); 9958c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x29, 0x4a); 9968c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x60, 0x2b); 9978c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x64, 0x01); 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci case ADM8211_AL2210L: 10018c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x00, 0x00); 10028c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x01, 0x00); 10038c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x02, 0x00); 10048c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x03, 0x00); 10058c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x06, 0x0f); 10068c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x07, 0x05); 10078c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x08, 0x03); 10088c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x09, 0x00); 10098c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0a, 0x00); 10108c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0b, 0x00); 10118c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0c, 0x10); 10128c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x0f, 0x55); 10138c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x10, 0x8d); 10148c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x11, 0x43); 10158c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x18, 0x4a); 10168c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x20, 0x20); 10178c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x21, 0x02); 10188c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x22, 0x23); 10198c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x23, 0x30); 10208c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x24, 0x2d); 10218c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2a, 0xaa); 10228c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2b, 0x81); 10238c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x2c, 0x44); 10248c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x29, 0xfa); 10258c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x60, 0x2d); 10268c2ecf20Sopenharmony_ci adm8211_write_bbp(dev, 0x64, 0x01); 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci case ADM8211_RFMD2948: 10308c2ecf20Sopenharmony_ci break; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci default: 10338c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, "unsupported transceiver %d\n", 10348c2ecf20Sopenharmony_ci priv->transceiver_type); 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci } else 10388c2ecf20Sopenharmony_ci wiphy_debug(dev->wiphy, "unsupported BBP %d\n", priv->bbp_type); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, 0); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* Set RF CAL control source to MAC control */ 10438c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(SYNCTL); 10448c2ecf20Sopenharmony_ci reg |= ADM8211_SYNCTL_SELCAL; 10458c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNCTL, reg); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return 0; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci/* configures hw beacons/probe responses */ 10518c2ecf20Sopenharmony_cistatic int adm8211_set_rate(struct ieee80211_hw *dev) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 10548c2ecf20Sopenharmony_ci u32 reg; 10558c2ecf20Sopenharmony_ci int i = 0; 10568c2ecf20Sopenharmony_ci u8 rate_buf[12] = {0}; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* write supported rates */ 10598c2ecf20Sopenharmony_ci if (priv->pdev->revision != ADM8211_REV_BA) { 10608c2ecf20Sopenharmony_ci rate_buf[0] = ARRAY_SIZE(adm8211_rates); 10618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++) 10628c2ecf20Sopenharmony_ci rate_buf[i + 1] = (adm8211_rates[i].bitrate / 5) | 0x80; 10638c2ecf20Sopenharmony_ci } else { 10648c2ecf20Sopenharmony_ci /* workaround for rev BA specific bug */ 10658c2ecf20Sopenharmony_ci rate_buf[0] = 0x04; 10668c2ecf20Sopenharmony_ci rate_buf[1] = 0x82; 10678c2ecf20Sopenharmony_ci rate_buf[2] = 0x04; 10688c2ecf20Sopenharmony_ci rate_buf[3] = 0x0b; 10698c2ecf20Sopenharmony_ci rate_buf[4] = 0x16; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, 10738c2ecf20Sopenharmony_ci ARRAY_SIZE(adm8211_rates) + 1); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF; /* keep bits 0-23 */ 10768c2ecf20Sopenharmony_ci reg |= 1 << 15; /* short preamble */ 10778c2ecf20Sopenharmony_ci reg |= 110 << 24; 10788c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PLCPHD, reg); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* MTMLT = 512 TU (max TX MSDU lifetime) 10818c2ecf20Sopenharmony_ci * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate) 10828c2ecf20Sopenharmony_ci * SRTYLIM = 224 (short retry limit, TX header value is default) */ 10838c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(TXLMT, (512 << 16) | (110 << 8) | (224 << 0)); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci return 0; 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic void adm8211_hw_init(struct ieee80211_hw *dev) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 10918c2ecf20Sopenharmony_ci u32 reg; 10928c2ecf20Sopenharmony_ci u8 cline; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(PAR); 10958c2ecf20Sopenharmony_ci reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME; 10968c2ecf20Sopenharmony_ci reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (!pci_set_mwi(priv->pdev)) { 10998c2ecf20Sopenharmony_ci reg |= 0x1 << 24; 11008c2ecf20Sopenharmony_ci pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cline); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci switch (cline) { 11038c2ecf20Sopenharmony_ci case 0x8: 11048c2ecf20Sopenharmony_ci reg |= (0x1 << 14); 11058c2ecf20Sopenharmony_ci break; 11068c2ecf20Sopenharmony_ci case 0x10: 11078c2ecf20Sopenharmony_ci reg |= (0x2 << 14); 11088c2ecf20Sopenharmony_ci break; 11098c2ecf20Sopenharmony_ci case 0x20: 11108c2ecf20Sopenharmony_ci reg |= (0x3 << 14); 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci default: 11138c2ecf20Sopenharmony_ci reg |= (0x0 << 14); 11148c2ecf20Sopenharmony_ci break; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PAR, reg); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CSR_TEST1); 11218c2ecf20Sopenharmony_ci reg &= ~(0xF << 28); 11228c2ecf20Sopenharmony_ci reg |= (1 << 28) | (1 << 31); 11238c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CSR_TEST1, reg); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* lose link after 4 lost beacons */ 11268c2ecf20Sopenharmony_ci reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE; 11278c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(WCSR, reg); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci /* Disable APM, enable receive FIFO threshold, and set drain receive 11308c2ecf20Sopenharmony_ci * threshold to store-and-forward */ 11318c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CMDR); 11328c2ecf20Sopenharmony_ci reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT); 11338c2ecf20Sopenharmony_ci reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF; 11348c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CMDR, reg); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci adm8211_set_rate(dev); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci /* 4-bit values: 11398c2ecf20Sopenharmony_ci * PWR1UP = 8 * 2 ms 11408c2ecf20Sopenharmony_ci * PWR0PAPE = 8 us or 5 us 11418c2ecf20Sopenharmony_ci * PWR1PAPE = 1 us or 3 us 11428c2ecf20Sopenharmony_ci * PWR0TRSW = 5 us 11438c2ecf20Sopenharmony_ci * PWR1TRSW = 12 us 11448c2ecf20Sopenharmony_ci * PWR0PE2 = 13 us 11458c2ecf20Sopenharmony_ci * PWR1PE2 = 1 us 11468c2ecf20Sopenharmony_ci * PWR0TXPE = 8 or 6 */ 11478c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_CA) 11488c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(TOFS2, 0x8815cd18); 11498c2ecf20Sopenharmony_ci else 11508c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(TOFS2, 0x8535cd16); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* Enable store and forward for transmit */ 11538c2ecf20Sopenharmony_ci priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB; 11548c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(NAR, priv->nar); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* Reset RF */ 11578c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO); 11588c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); 11598c2ecf20Sopenharmony_ci msleep(10); 11608c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(SYNRF, 0); 11618c2ecf20Sopenharmony_ci ADM8211_CSR_READ(SYNRF); 11628c2ecf20Sopenharmony_ci msleep(5); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* Set CFP Max Duration to 0x10 TU */ 11658c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CFPP); 11668c2ecf20Sopenharmony_ci reg &= ~(0xffff << 8); 11678c2ecf20Sopenharmony_ci reg |= 0x0010 << 8; 11688c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CFPP, reg); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us 11718c2ecf20Sopenharmony_ci * TUCNT = 0x3ff - Tu counter 1024 us */ 11728c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us), 11758c2ecf20Sopenharmony_ci * DIFS=50 us, EIFS=100 us */ 11768c2ecf20Sopenharmony_ci if (priv->pdev->revision < ADM8211_REV_CA) 11778c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) | 11788c2ecf20Sopenharmony_ci (50 << 9) | 100); 11798c2ecf20Sopenharmony_ci else 11808c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) | 11818c2ecf20Sopenharmony_ci (50 << 9) | 100); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* PCNT = 1 (MAC idle time awake/sleep, unit S) 11848c2ecf20Sopenharmony_ci * RMRD = 2346 * 8 + 1 us (max RX duration) */ 11858c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci /* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */ 11888c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(RSPT, 0xffffff00); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Initialize BBP (and SYN) */ 11918c2ecf20Sopenharmony_ci adm8211_hw_init_bbp(dev); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* make sure interrupts are off */ 11948c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(IER, 0); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* ACK interrupts */ 11978c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR)); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* Setup WEP (turns it off for now) */ 12008c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(MACTEST); 12018c2ecf20Sopenharmony_ci reg &= ~(7 << 20); 12028c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MACTEST, reg); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(WEPCTL); 12058c2ecf20Sopenharmony_ci reg &= ~ADM8211_WEPCTL_WEPENABLE; 12068c2ecf20Sopenharmony_ci reg |= ADM8211_WEPCTL_WEPRXBYP; 12078c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(WEPCTL, reg); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* Clear the missed-packet counter. */ 12108c2ecf20Sopenharmony_ci ADM8211_CSR_READ(LPC); 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic int adm8211_hw_reset(struct ieee80211_hw *dev) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 12168c2ecf20Sopenharmony_ci u32 reg, tmp; 12178c2ecf20Sopenharmony_ci int timeout = 100; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* Power-on issue */ 12208c2ecf20Sopenharmony_ci /* TODO: check if this is necessary */ 12218c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(FRCTL, 0); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Reset the chip */ 12248c2ecf20Sopenharmony_ci tmp = ADM8211_CSR_READ(PAR); 12258c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout--) 12288c2ecf20Sopenharmony_ci msleep(50); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (timeout <= 0) 12318c2ecf20Sopenharmony_ci return -ETIMEDOUT; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PAR, tmp); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (priv->pdev->revision == ADM8211_REV_BA && 12368c2ecf20Sopenharmony_ci (priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || 12378c2ecf20Sopenharmony_ci priv->transceiver_type == ADM8211_RFMD2958)) { 12388c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CSR_TEST1); 12398c2ecf20Sopenharmony_ci reg |= (1 << 4) | (1 << 5); 12408c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CSR_TEST1, reg); 12418c2ecf20Sopenharmony_ci } else if (priv->pdev->revision == ADM8211_REV_CA) { 12428c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CSR_TEST1); 12438c2ecf20Sopenharmony_ci reg &= ~((1 << 4) | (1 << 5)); 12448c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CSR_TEST1, reg); 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(FRCTL, 0); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(CSR_TEST0); 12508c2ecf20Sopenharmony_ci reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */ 12518c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(CSR_TEST0, reg); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci adm8211_clear_sram(dev); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci return 0; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic u64 adm8211_get_tsft(struct ieee80211_hw *dev, 12598c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 12628c2ecf20Sopenharmony_ci u32 tsftl; 12638c2ecf20Sopenharmony_ci u64 tsft; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci tsftl = ADM8211_CSR_READ(TSFTL); 12668c2ecf20Sopenharmony_ci tsft = ADM8211_CSR_READ(TSFTH); 12678c2ecf20Sopenharmony_ci tsft <<= 32; 12688c2ecf20Sopenharmony_ci tsft |= tsftl; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci return tsft; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void adm8211_set_interval(struct ieee80211_hw *dev, 12748c2ecf20Sopenharmony_ci unsigned short bi, unsigned short li) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 12778c2ecf20Sopenharmony_ci u32 reg; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* BP (beacon interval) = data->beacon_interval 12808c2ecf20Sopenharmony_ci * LI (listen interval) = data->listen_interval (in beacon intervals) */ 12818c2ecf20Sopenharmony_ci reg = (bi << 16) | li; 12828c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(BPLI, reg); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cistatic void adm8211_set_bssid(struct ieee80211_hw *dev, const u8 *bssid) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 12888c2ecf20Sopenharmony_ci u32 reg; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(BSSID0, le32_to_cpu(*(__le32 *)bssid)); 12918c2ecf20Sopenharmony_ci reg = ADM8211_CSR_READ(ABDA1); 12928c2ecf20Sopenharmony_ci reg &= 0x0000ffff; 12938c2ecf20Sopenharmony_ci reg |= (bssid[4] << 16) | (bssid[5] << 24); 12948c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(ABDA1, reg); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int adm8211_config(struct ieee80211_hw *dev, u32 changed) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 13008c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &dev->conf; 13018c2ecf20Sopenharmony_ci int channel = 13028c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(conf->chandef.chan->center_freq); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (channel != priv->channel) { 13058c2ecf20Sopenharmony_ci priv->channel = channel; 13068c2ecf20Sopenharmony_ci adm8211_rf_set_channel(dev, priv->channel); 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic void adm8211_bss_info_changed(struct ieee80211_hw *dev, 13138c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 13148c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *conf, 13158c2ecf20Sopenharmony_ci u32 changes) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (!(changes & BSS_CHANGED_BSSID)) 13208c2ecf20Sopenharmony_ci return; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (!ether_addr_equal(conf->bssid, priv->bssid)) { 13238c2ecf20Sopenharmony_ci adm8211_set_bssid(dev, conf->bssid); 13248c2ecf20Sopenharmony_ci memcpy(priv->bssid, conf->bssid, ETH_ALEN); 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, 13298c2ecf20Sopenharmony_ci struct netdev_hw_addr_list *mc_list) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci unsigned int bit_nr; 13328c2ecf20Sopenharmony_ci u32 mc_filter[2]; 13338c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci netdev_hw_addr_list_for_each(ha, mc_list) { 13388c2ecf20Sopenharmony_ci bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci bit_nr &= 0x3F; 13418c2ecf20Sopenharmony_ci mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return mc_filter[0] | ((u64)(mc_filter[1]) << 32); 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic void adm8211_configure_filter(struct ieee80211_hw *dev, 13488c2ecf20Sopenharmony_ci unsigned int changed_flags, 13498c2ecf20Sopenharmony_ci unsigned int *total_flags, 13508c2ecf20Sopenharmony_ci u64 multicast) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 13538c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 13548c2ecf20Sopenharmony_ci unsigned int new_flags; 13558c2ecf20Sopenharmony_ci u32 mc_filter[2]; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci mc_filter[0] = multicast; 13588c2ecf20Sopenharmony_ci mc_filter[1] = multicast >> 32; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci new_flags = 0; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { 13638c2ecf20Sopenharmony_ci new_flags |= FIF_ALLMULTI; 13648c2ecf20Sopenharmony_ci priv->nar &= ~ADM8211_NAR_PR; 13658c2ecf20Sopenharmony_ci priv->nar |= ADM8211_NAR_MM; 13668c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = ~0; 13678c2ecf20Sopenharmony_ci } else { 13688c2ecf20Sopenharmony_ci priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci ADM8211_IDLE_RX(); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MAR0, mc_filter[0]); 13748c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(MAR1, mc_filter[1]); 13758c2ecf20Sopenharmony_ci ADM8211_CSR_READ(NAR); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (priv->nar & ADM8211_NAR_PR) 13788c2ecf20Sopenharmony_ci ieee80211_hw_set(dev, RX_INCLUDES_FCS); 13798c2ecf20Sopenharmony_ci else 13808c2ecf20Sopenharmony_ci __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, dev->flags); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (*total_flags & FIF_BCN_PRBRESP_PROMISC) 13838c2ecf20Sopenharmony_ci adm8211_set_bssid(dev, bcast); 13848c2ecf20Sopenharmony_ci else 13858c2ecf20Sopenharmony_ci adm8211_set_bssid(dev, priv->bssid); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci ADM8211_RESTORE(); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci *total_flags = new_flags; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cistatic int adm8211_add_interface(struct ieee80211_hw *dev, 13938c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 13968c2ecf20Sopenharmony_ci if (priv->mode != NL80211_IFTYPE_MONITOR) 13978c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci switch (vif->type) { 14008c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 14018c2ecf20Sopenharmony_ci priv->mode = vif->type; 14028c2ecf20Sopenharmony_ci break; 14038c2ecf20Sopenharmony_ci default: 14048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci ADM8211_IDLE(); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr)); 14108c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4))); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci adm8211_update_mode(dev); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci ADM8211_RESTORE(); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci return 0; 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic void adm8211_remove_interface(struct ieee80211_hw *dev, 14208c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 14238c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_MONITOR; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int adm8211_init_rings(struct ieee80211_hw *dev) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 14298c2ecf20Sopenharmony_ci struct adm8211_desc *desc = NULL; 14308c2ecf20Sopenharmony_ci struct adm8211_rx_ring_info *rx_info; 14318c2ecf20Sopenharmony_ci struct adm8211_tx_ring_info *tx_info; 14328c2ecf20Sopenharmony_ci unsigned int i; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 14358c2ecf20Sopenharmony_ci desc = &priv->rx_ring[i]; 14368c2ecf20Sopenharmony_ci desc->status = 0; 14378c2ecf20Sopenharmony_ci desc->length = cpu_to_le32(RX_PKT_SIZE); 14388c2ecf20Sopenharmony_ci priv->rx_buffers[i].skb = NULL; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci /* Mark the end of RX ring; hw returns to base address after this 14418c2ecf20Sopenharmony_ci * descriptor */ 14428c2ecf20Sopenharmony_ci desc->length |= cpu_to_le32(RDES1_CONTROL_RER); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 14458c2ecf20Sopenharmony_ci desc = &priv->rx_ring[i]; 14468c2ecf20Sopenharmony_ci rx_info = &priv->rx_buffers[i]; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci rx_info->skb = dev_alloc_skb(RX_PKT_SIZE); 14498c2ecf20Sopenharmony_ci if (rx_info->skb == NULL) 14508c2ecf20Sopenharmony_ci break; 14518c2ecf20Sopenharmony_ci rx_info->mapping = dma_map_single(&priv->pdev->dev, 14528c2ecf20Sopenharmony_ci skb_tail_pointer(rx_info->skb), 14538c2ecf20Sopenharmony_ci RX_PKT_SIZE, 14548c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 14558c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, rx_info->mapping)) { 14568c2ecf20Sopenharmony_ci dev_kfree_skb(rx_info->skb); 14578c2ecf20Sopenharmony_ci rx_info->skb = NULL; 14588c2ecf20Sopenharmony_ci break; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci desc->buffer1 = cpu_to_le32(rx_info->mapping); 14628c2ecf20Sopenharmony_ci desc->status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL); 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* Setup TX ring. TX buffers descriptors will be filled in as needed */ 14668c2ecf20Sopenharmony_ci for (i = 0; i < priv->tx_ring_size; i++) { 14678c2ecf20Sopenharmony_ci desc = &priv->tx_ring[i]; 14688c2ecf20Sopenharmony_ci tx_info = &priv->tx_buffers[i]; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci tx_info->skb = NULL; 14718c2ecf20Sopenharmony_ci tx_info->mapping = 0; 14728c2ecf20Sopenharmony_ci desc->status = 0; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci desc->length = cpu_to_le32(TDES1_CONTROL_TER); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0; 14778c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma); 14788c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci return 0; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic void adm8211_free_rings(struct ieee80211_hw *dev) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 14868c2ecf20Sopenharmony_ci unsigned int i; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci for (i = 0; i < priv->rx_ring_size; i++) { 14898c2ecf20Sopenharmony_ci if (!priv->rx_buffers[i].skb) 14908c2ecf20Sopenharmony_ci continue; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 14938c2ecf20Sopenharmony_ci priv->rx_buffers[i].mapping, RX_PKT_SIZE, 14948c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci dev_kfree_skb(priv->rx_buffers[i].skb); 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci for (i = 0; i < priv->tx_ring_size; i++) { 15008c2ecf20Sopenharmony_ci if (!priv->tx_buffers[i].skb) 15018c2ecf20Sopenharmony_ci continue; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 15048c2ecf20Sopenharmony_ci priv->tx_buffers[i].mapping, 15058c2ecf20Sopenharmony_ci priv->tx_buffers[i].skb->len, DMA_TO_DEVICE); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci dev_kfree_skb(priv->tx_buffers[i].skb); 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic int adm8211_start(struct ieee80211_hw *dev) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 15148c2ecf20Sopenharmony_ci int retval; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* Power up MAC and RF chips */ 15178c2ecf20Sopenharmony_ci retval = adm8211_hw_reset(dev); 15188c2ecf20Sopenharmony_ci if (retval) { 15198c2ecf20Sopenharmony_ci wiphy_err(dev->wiphy, "hardware reset failed\n"); 15208c2ecf20Sopenharmony_ci goto fail; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci retval = adm8211_init_rings(dev); 15248c2ecf20Sopenharmony_ci if (retval) { 15258c2ecf20Sopenharmony_ci wiphy_err(dev->wiphy, "failed to initialize rings\n"); 15268c2ecf20Sopenharmony_ci goto fail; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci /* Init hardware */ 15308c2ecf20Sopenharmony_ci adm8211_hw_init(dev); 15318c2ecf20Sopenharmony_ci adm8211_rf_set_channel(dev, priv->channel); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci retval = request_irq(priv->pdev->irq, adm8211_interrupt, 15348c2ecf20Sopenharmony_ci IRQF_SHARED, "adm8211", dev); 15358c2ecf20Sopenharmony_ci if (retval) { 15368c2ecf20Sopenharmony_ci wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); 15378c2ecf20Sopenharmony_ci goto fail; 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(IER, ADM8211_IER_NIE | ADM8211_IER_AIE | 15418c2ecf20Sopenharmony_ci ADM8211_IER_RCIE | ADM8211_IER_TCIE | 15428c2ecf20Sopenharmony_ci ADM8211_IER_TDUIE | ADM8211_IER_GPTIE); 15438c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_MONITOR; 15448c2ecf20Sopenharmony_ci adm8211_update_mode(dev); 15458c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(RDR, 0); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci adm8211_set_interval(dev, 100, 10); 15488c2ecf20Sopenharmony_ci return 0; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cifail: 15518c2ecf20Sopenharmony_ci return retval; 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_cistatic void adm8211_stop(struct ieee80211_hw *dev) 15558c2ecf20Sopenharmony_ci{ 15568c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_UNSPECIFIED; 15598c2ecf20Sopenharmony_ci priv->nar = 0; 15608c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(NAR, 0); 15618c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(IER, 0); 15628c2ecf20Sopenharmony_ci ADM8211_CSR_READ(NAR); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci free_irq(priv->pdev->irq, dev); 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci adm8211_free_rings(dev); 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_cistatic void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len, 15708c2ecf20Sopenharmony_ci int plcp_signal, int short_preamble) 15718c2ecf20Sopenharmony_ci{ 15728c2ecf20Sopenharmony_ci /* Alternative calculation from NetBSD: */ 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci/* IEEE 802.11b durations for DSSS PHY in microseconds */ 15758c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 15768c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 15778c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 15788c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 15798c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_SLOW_ACK 112 15808c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_FAST_ACK 56 15818c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_SLOW_CTS 112 15828c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_FAST_CTS 56 15838c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_SLOT 20 15848c2ecf20Sopenharmony_ci#define IEEE80211_DUR_DS_SIFS 10 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci int remainder; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci *dur = (80 * (24 + payload_len) + plcp_signal - 1) 15898c2ecf20Sopenharmony_ci / plcp_signal; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (plcp_signal <= PLCP_SIGNAL_2M) 15928c2ecf20Sopenharmony_ci /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */ 15938c2ecf20Sopenharmony_ci *dur += 3 * (IEEE80211_DUR_DS_SIFS + 15948c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_SHORT_PREAMBLE + 15958c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_FAST_PLCPHDR) + 15968c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK; 15978c2ecf20Sopenharmony_ci else 15988c2ecf20Sopenharmony_ci /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */ 15998c2ecf20Sopenharmony_ci *dur += 3 * (IEEE80211_DUR_DS_SIFS + 16008c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_SHORT_PREAMBLE + 16018c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_FAST_PLCPHDR) + 16028c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci /* lengthen duration if long preamble */ 16058c2ecf20Sopenharmony_ci if (!short_preamble) 16068c2ecf20Sopenharmony_ci *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE - 16078c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_SHORT_PREAMBLE) + 16088c2ecf20Sopenharmony_ci 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR - 16098c2ecf20Sopenharmony_ci IEEE80211_DUR_DS_FAST_PLCPHDR); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci *plcp = (80 * len) / plcp_signal; 16138c2ecf20Sopenharmony_ci remainder = (80 * len) % plcp_signal; 16148c2ecf20Sopenharmony_ci if (plcp_signal == PLCP_SIGNAL_11M && 16158c2ecf20Sopenharmony_ci remainder <= 30 && remainder > 0) 16168c2ecf20Sopenharmony_ci *plcp = (*plcp | 0x8000) + 1; 16178c2ecf20Sopenharmony_ci else if (remainder) 16188c2ecf20Sopenharmony_ci (*plcp)++; 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */ 16228c2ecf20Sopenharmony_cistatic int adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, 16238c2ecf20Sopenharmony_ci u16 plcp_signal, 16248c2ecf20Sopenharmony_ci size_t hdrlen) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 16278c2ecf20Sopenharmony_ci unsigned long flags; 16288c2ecf20Sopenharmony_ci dma_addr_t mapping; 16298c2ecf20Sopenharmony_ci unsigned int entry; 16308c2ecf20Sopenharmony_ci u32 flag; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci mapping = dma_map_single(&priv->pdev->dev, skb->data, skb->len, 16338c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 16348c2ecf20Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, mapping)) 16358c2ecf20Sopenharmony_ci return -ENOMEM; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2) 16408c2ecf20Sopenharmony_ci flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS; 16418c2ecf20Sopenharmony_ci else 16428c2ecf20Sopenharmony_ci flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2) 16458c2ecf20Sopenharmony_ci ieee80211_stop_queue(dev, 0); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci entry = priv->cur_tx % priv->tx_ring_size; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci priv->tx_buffers[entry].skb = skb; 16508c2ecf20Sopenharmony_ci priv->tx_buffers[entry].mapping = mapping; 16518c2ecf20Sopenharmony_ci priv->tx_buffers[entry].hdrlen = hdrlen; 16528c2ecf20Sopenharmony_ci priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (entry == priv->tx_ring_size - 1) 16558c2ecf20Sopenharmony_ci flag |= TDES1_CONTROL_TER; 16568c2ecf20Sopenharmony_ci priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* Set TX rate (SIGNAL field in PLCP PPDU format) */ 16598c2ecf20Sopenharmony_ci flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */; 16608c2ecf20Sopenharmony_ci priv->tx_ring[entry].status = cpu_to_le32(flag); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci priv->cur_tx++; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci /* Trigger transmit poll */ 16678c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(TDR, 0); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci return 0; 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci/* Put adm8211_tx_hdr on skb and transmit */ 16738c2ecf20Sopenharmony_cistatic void adm8211_tx(struct ieee80211_hw *dev, 16748c2ecf20Sopenharmony_ci struct ieee80211_tx_control *control, 16758c2ecf20Sopenharmony_ci struct sk_buff *skb) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct adm8211_tx_hdr *txhdr; 16788c2ecf20Sopenharmony_ci size_t payload_len, hdrlen; 16798c2ecf20Sopenharmony_ci int plcp, dur, len, plcp_signal, short_preamble; 16808c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 16818c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 16828c2ecf20Sopenharmony_ci struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info); 16838c2ecf20Sopenharmony_ci u8 rc_flags; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci rc_flags = info->control.rates[0].flags; 16868c2ecf20Sopenharmony_ci short_preamble = !!(rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE); 16878c2ecf20Sopenharmony_ci plcp_signal = txrate->bitrate; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 16908c2ecf20Sopenharmony_ci hdrlen = ieee80211_hdrlen(hdr->frame_control); 16918c2ecf20Sopenharmony_ci memcpy(skb->cb, skb->data, hdrlen); 16928c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->cb; 16938c2ecf20Sopenharmony_ci skb_pull(skb, hdrlen); 16948c2ecf20Sopenharmony_ci payload_len = skb->len; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci txhdr = skb_push(skb, sizeof(*txhdr)); 16978c2ecf20Sopenharmony_ci memset(txhdr, 0, sizeof(*txhdr)); 16988c2ecf20Sopenharmony_ci memcpy(txhdr->da, ieee80211_get_DA(hdr), ETH_ALEN); 16998c2ecf20Sopenharmony_ci txhdr->signal = plcp_signal; 17008c2ecf20Sopenharmony_ci txhdr->frame_body_size = cpu_to_le16(payload_len); 17018c2ecf20Sopenharmony_ci txhdr->frame_control = hdr->frame_control; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci len = hdrlen + payload_len + FCS_LEN; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci txhdr->frag = cpu_to_le16(0x0FFF); 17068c2ecf20Sopenharmony_ci adm8211_calc_durations(&dur, &plcp, payload_len, 17078c2ecf20Sopenharmony_ci len, plcp_signal, short_preamble); 17088c2ecf20Sopenharmony_ci txhdr->plcp_frag_head_len = cpu_to_le16(plcp); 17098c2ecf20Sopenharmony_ci txhdr->plcp_frag_tail_len = cpu_to_le16(plcp); 17108c2ecf20Sopenharmony_ci txhdr->dur_frag_head = cpu_to_le16(dur); 17118c2ecf20Sopenharmony_ci txhdr->dur_frag_tail = cpu_to_le16(dur); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (short_preamble) 17168c2ecf20Sopenharmony_ci txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) 17198c2ecf20Sopenharmony_ci txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci txhdr->retry_limit = info->control.rates[0].count; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (adm8211_tx_raw(dev, skb, plcp_signal, hdrlen)) { 17248c2ecf20Sopenharmony_ci /* Drop packet */ 17258c2ecf20Sopenharmony_ci ieee80211_free_txskb(dev, skb); 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_cistatic int adm8211_alloc_rings(struct ieee80211_hw *dev) 17308c2ecf20Sopenharmony_ci{ 17318c2ecf20Sopenharmony_ci struct adm8211_priv *priv = dev->priv; 17328c2ecf20Sopenharmony_ci unsigned int ring_size; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci priv->rx_buffers = kmalloc(sizeof(*priv->rx_buffers) * priv->rx_ring_size + 17358c2ecf20Sopenharmony_ci sizeof(*priv->tx_buffers) * priv->tx_ring_size, GFP_KERNEL); 17368c2ecf20Sopenharmony_ci if (!priv->rx_buffers) 17378c2ecf20Sopenharmony_ci return -ENOMEM; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci priv->tx_buffers = (void *)priv->rx_buffers + 17408c2ecf20Sopenharmony_ci sizeof(*priv->rx_buffers) * priv->rx_ring_size; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci /* Allocate TX/RX descriptors */ 17438c2ecf20Sopenharmony_ci ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + 17448c2ecf20Sopenharmony_ci sizeof(struct adm8211_desc) * priv->tx_ring_size; 17458c2ecf20Sopenharmony_ci priv->rx_ring = dma_alloc_coherent(&priv->pdev->dev, ring_size, 17468c2ecf20Sopenharmony_ci &priv->rx_ring_dma, GFP_KERNEL); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (!priv->rx_ring) { 17498c2ecf20Sopenharmony_ci kfree(priv->rx_buffers); 17508c2ecf20Sopenharmony_ci priv->rx_buffers = NULL; 17518c2ecf20Sopenharmony_ci priv->tx_buffers = NULL; 17528c2ecf20Sopenharmony_ci return -ENOMEM; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci priv->tx_ring = priv->rx_ring + priv->rx_ring_size; 17568c2ecf20Sopenharmony_ci priv->tx_ring_dma = priv->rx_ring_dma + 17578c2ecf20Sopenharmony_ci sizeof(struct adm8211_desc) * priv->rx_ring_size; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci return 0; 17608c2ecf20Sopenharmony_ci} 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_cistatic const struct ieee80211_ops adm8211_ops = { 17638c2ecf20Sopenharmony_ci .tx = adm8211_tx, 17648c2ecf20Sopenharmony_ci .start = adm8211_start, 17658c2ecf20Sopenharmony_ci .stop = adm8211_stop, 17668c2ecf20Sopenharmony_ci .add_interface = adm8211_add_interface, 17678c2ecf20Sopenharmony_ci .remove_interface = adm8211_remove_interface, 17688c2ecf20Sopenharmony_ci .config = adm8211_config, 17698c2ecf20Sopenharmony_ci .bss_info_changed = adm8211_bss_info_changed, 17708c2ecf20Sopenharmony_ci .prepare_multicast = adm8211_prepare_multicast, 17718c2ecf20Sopenharmony_ci .configure_filter = adm8211_configure_filter, 17728c2ecf20Sopenharmony_ci .get_stats = adm8211_get_stats, 17738c2ecf20Sopenharmony_ci .get_tsf = adm8211_get_tsft 17748c2ecf20Sopenharmony_ci}; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_cistatic int adm8211_probe(struct pci_dev *pdev, 17778c2ecf20Sopenharmony_ci const struct pci_device_id *id) 17788c2ecf20Sopenharmony_ci{ 17798c2ecf20Sopenharmony_ci struct ieee80211_hw *dev; 17808c2ecf20Sopenharmony_ci struct adm8211_priv *priv; 17818c2ecf20Sopenharmony_ci unsigned long mem_len; 17828c2ecf20Sopenharmony_ci unsigned int io_len; 17838c2ecf20Sopenharmony_ci int err; 17848c2ecf20Sopenharmony_ci u32 reg; 17858c2ecf20Sopenharmony_ci u8 perm_addr[ETH_ALEN]; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 17888c2ecf20Sopenharmony_ci if (err) { 17898c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", 17908c2ecf20Sopenharmony_ci pci_name(pdev)); 17918c2ecf20Sopenharmony_ci return err; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci io_len = pci_resource_len(pdev, 0); 17958c2ecf20Sopenharmony_ci mem_len = pci_resource_len(pdev, 1); 17968c2ecf20Sopenharmony_ci if (io_len < 256 || mem_len < 1024) { 17978c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", 17988c2ecf20Sopenharmony_ci pci_name(pdev)); 17998c2ecf20Sopenharmony_ci err = -ENOMEM; 18008c2ecf20Sopenharmony_ci goto err_disable_pdev; 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci /* check signature */ 18058c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, 0x80 /* CR32 */, ®); 18068c2ecf20Sopenharmony_ci if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) { 18078c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", 18088c2ecf20Sopenharmony_ci pci_name(pdev), reg); 18098c2ecf20Sopenharmony_ci err = -EINVAL; 18108c2ecf20Sopenharmony_ci goto err_disable_pdev; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, "adm8211"); 18148c2ecf20Sopenharmony_ci if (err) { 18158c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", 18168c2ecf20Sopenharmony_ci pci_name(pdev)); 18178c2ecf20Sopenharmony_ci return err; /* someone else grabbed it? don't disable it */ 18188c2ecf20Sopenharmony_ci } 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 18218c2ecf20Sopenharmony_ci if (err) { 18228c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", 18238c2ecf20Sopenharmony_ci pci_name(pdev)); 18248c2ecf20Sopenharmony_ci goto err_free_reg; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci pci_set_master(pdev); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops); 18308c2ecf20Sopenharmony_ci if (!dev) { 18318c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", 18328c2ecf20Sopenharmony_ci pci_name(pdev)); 18338c2ecf20Sopenharmony_ci err = -ENOMEM; 18348c2ecf20Sopenharmony_ci goto err_free_reg; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci priv = dev->priv; 18378c2ecf20Sopenharmony_ci priv->pdev = pdev; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(dev, &pdev->dev); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci priv->map = pci_iomap(pdev, 1, mem_len); 18468c2ecf20Sopenharmony_ci if (!priv->map) 18478c2ecf20Sopenharmony_ci priv->map = pci_iomap(pdev, 0, io_len); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (!priv->map) { 18508c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", 18518c2ecf20Sopenharmony_ci pci_name(pdev)); 18528c2ecf20Sopenharmony_ci err = -ENOMEM; 18538c2ecf20Sopenharmony_ci goto err_free_dev; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci priv->rx_ring_size = rx_ring_size; 18578c2ecf20Sopenharmony_ci priv->tx_ring_size = tx_ring_size; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci err = adm8211_alloc_rings(dev); 18608c2ecf20Sopenharmony_ci if (err) { 18618c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", 18628c2ecf20Sopenharmony_ci pci_name(pdev)); 18638c2ecf20Sopenharmony_ci goto err_iounmap; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci *(__le32 *)perm_addr = cpu_to_le32(ADM8211_CSR_READ(PAR0)); 18678c2ecf20Sopenharmony_ci *(__le16 *)&perm_addr[4] = 18688c2ecf20Sopenharmony_ci cpu_to_le16(ADM8211_CSR_READ(PAR1) & 0xFFFF); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(perm_addr)) { 18718c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s (adm8211): Invalid hwaddr in EEPROM!\n", 18728c2ecf20Sopenharmony_ci pci_name(pdev)); 18738c2ecf20Sopenharmony_ci eth_random_addr(perm_addr); 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci SET_IEEE80211_PERM_ADDR(dev, perm_addr); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); 18788c2ecf20Sopenharmony_ci /* dev->flags = RX_INCLUDES_FCS in promisc mode */ 18798c2ecf20Sopenharmony_ci ieee80211_hw_set(dev, SIGNAL_UNSPEC); 18808c2ecf20Sopenharmony_ci dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci dev->max_signal = 100; /* FIXME: find better value */ 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci priv->retry_limit = 3; 18878c2ecf20Sopenharmony_ci priv->ant_power = 0x40; 18888c2ecf20Sopenharmony_ci priv->tx_power = 0x40; 18898c2ecf20Sopenharmony_ci priv->lpf_cutoff = 0xFF; 18908c2ecf20Sopenharmony_ci priv->lnags_threshold = 0xFF; 18918c2ecf20Sopenharmony_ci priv->mode = NL80211_IFTYPE_UNSPECIFIED; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* Power-on issue. EEPROM won't read correctly without */ 18948c2ecf20Sopenharmony_ci if (pdev->revision >= ADM8211_REV_BA) { 18958c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(FRCTL, 0); 18968c2ecf20Sopenharmony_ci ADM8211_CSR_READ(FRCTL); 18978c2ecf20Sopenharmony_ci ADM8211_CSR_WRITE(FRCTL, 1); 18988c2ecf20Sopenharmony_ci ADM8211_CSR_READ(FRCTL); 18998c2ecf20Sopenharmony_ci msleep(100); 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci err = adm8211_read_eeprom(dev); 19038c2ecf20Sopenharmony_ci if (err) { 19048c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Can't alloc eeprom buffer\n", 19058c2ecf20Sopenharmony_ci pci_name(pdev)); 19068c2ecf20Sopenharmony_ci goto err_free_desc; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci priv->channel = 1; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci dev->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci err = ieee80211_register_hw(dev); 19168c2ecf20Sopenharmony_ci if (err) { 19178c2ecf20Sopenharmony_ci printk(KERN_ERR "%s (adm8211): Cannot register device\n", 19188c2ecf20Sopenharmony_ci pci_name(pdev)); 19198c2ecf20Sopenharmony_ci goto err_free_eeprom; 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci wiphy_info(dev->wiphy, "hwaddr %pM, Rev 0x%02x\n", 19238c2ecf20Sopenharmony_ci dev->wiphy->perm_addr, pdev->revision); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci return 0; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci err_free_eeprom: 19288c2ecf20Sopenharmony_ci kfree(priv->eeprom); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci err_free_desc: 19318c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, 19328c2ecf20Sopenharmony_ci sizeof(struct adm8211_desc) * priv->rx_ring_size + 19338c2ecf20Sopenharmony_ci sizeof(struct adm8211_desc) * priv->tx_ring_size, 19348c2ecf20Sopenharmony_ci priv->rx_ring, priv->rx_ring_dma); 19358c2ecf20Sopenharmony_ci kfree(priv->rx_buffers); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci err_iounmap: 19388c2ecf20Sopenharmony_ci pci_iounmap(pdev, priv->map); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci err_free_dev: 19418c2ecf20Sopenharmony_ci ieee80211_free_hw(dev); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci err_free_reg: 19448c2ecf20Sopenharmony_ci pci_release_regions(pdev); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci err_disable_pdev: 19478c2ecf20Sopenharmony_ci pci_disable_device(pdev); 19488c2ecf20Sopenharmony_ci return err; 19498c2ecf20Sopenharmony_ci} 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_cistatic void adm8211_remove(struct pci_dev *pdev) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = pci_get_drvdata(pdev); 19558c2ecf20Sopenharmony_ci struct adm8211_priv *priv; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci if (!dev) 19588c2ecf20Sopenharmony_ci return; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci ieee80211_unregister_hw(dev); 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci priv = dev->priv; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, 19658c2ecf20Sopenharmony_ci sizeof(struct adm8211_desc) * priv->rx_ring_size + 19668c2ecf20Sopenharmony_ci sizeof(struct adm8211_desc) * priv->tx_ring_size, 19678c2ecf20Sopenharmony_ci priv->rx_ring, priv->rx_ring_dma); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci kfree(priv->rx_buffers); 19708c2ecf20Sopenharmony_ci kfree(priv->eeprom); 19718c2ecf20Sopenharmony_ci pci_iounmap(pdev, priv->map); 19728c2ecf20Sopenharmony_ci pci_release_regions(pdev); 19738c2ecf20Sopenharmony_ci pci_disable_device(pdev); 19748c2ecf20Sopenharmony_ci ieee80211_free_hw(dev); 19758c2ecf20Sopenharmony_ci} 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci#define adm8211_suspend NULL 19798c2ecf20Sopenharmony_ci#define adm8211_resume NULL 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, adm8211_pci_id_table); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(adm8211_pm_ops, adm8211_suspend, adm8211_resume); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci/* TODO: implement enable_wake */ 19868c2ecf20Sopenharmony_cistatic struct pci_driver adm8211_driver = { 19878c2ecf20Sopenharmony_ci .name = "adm8211", 19888c2ecf20Sopenharmony_ci .id_table = adm8211_pci_id_table, 19898c2ecf20Sopenharmony_ci .probe = adm8211_probe, 19908c2ecf20Sopenharmony_ci .remove = adm8211_remove, 19918c2ecf20Sopenharmony_ci .driver.pm = &adm8211_pm_ops, 19928c2ecf20Sopenharmony_ci}; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_cimodule_pci_driver(adm8211_driver); 1995