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 */, &reg);
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