summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath9k/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/init.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c218
1 files changed, 133 insertions, 85 deletions
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index fd9a61834c17..ee951493e993 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -18,11 +18,12 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/ath9k_platform.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_net.h>
+#include <linux/nvmem-consumer.h>
#include <linux/relay.h>
+#include <linux/dmi.h>
#include <net/ieee80211_radiotap.h>
#include "ath9k.h"
@@ -36,7 +37,6 @@ static char *dev_info = "ath9k";
MODULE_AUTHOR("Atheros Communications");
MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
-MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
MODULE_LICENSE("Dual BSD/GPL");
static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
@@ -75,6 +75,10 @@ MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
+int ath9k_use_msi;
+module_param_named(use_msi, ath9k_use_msi, int, 0444);
+MODULE_PARM_DESC(use_msi, "Use MSI instead of INTx if possible");
+
bool is_ath9k_unloaded;
#ifdef CONFIG_MAC80211_LEDS
@@ -92,19 +96,69 @@ static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = {
};
#endif
+static int __init set_use_msi(const struct dmi_system_id *dmi)
+{
+ ath9k_use_msi = 1;
+ return 1;
+}
+
+static const struct dmi_system_id ath9k_quirks[] __initconst = {
+ {
+ .callback = set_use_msi,
+ .ident = "Dell Inspiron 24-3460",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 24-3460"),
+ },
+ },
+ {
+ .callback = set_use_msi,
+ .ident = "Dell Vostro 3262",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3262"),
+ },
+ },
+ {
+ .callback = set_use_msi,
+ .ident = "Dell Inspiron 3472",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3472"),
+ },
+ },
+ {
+ .callback = set_use_msi,
+ .ident = "Dell Vostro 15-3572",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15-3572"),
+ },
+ },
+ {
+ .callback = set_use_msi,
+ .ident = "Dell Inspiron 14-3473",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 14-3473"),
+ },
+ },
+ {}
+};
+
static void ath9k_deinit_softc(struct ath_softc *sc);
static void ath9k_op_ps_wakeup(struct ath_common *common)
{
- ath9k_ps_wakeup((struct ath_softc *) common->priv);
+ ath9k_ps_wakeup(common->priv);
}
static void ath9k_op_ps_restore(struct ath_common *common)
{
- ath9k_ps_restore((struct ath_softc *) common->priv);
+ ath9k_ps_restore(common->priv);
}
-static struct ath_ps_ops ath9k_ps_ops = {
+static const struct ath_ps_ops ath9k_ps_ops = {
.wakeup = ath9k_op_ps_wakeup,
.restore = ath9k_op_ps_restore,
};
@@ -117,9 +171,9 @@ static struct ath_ps_ops ath9k_ps_ops = {
static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
{
- struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_hw *ah = hw_priv;
struct ath_common *common = ath9k_hw_common(ah);
- struct ath_softc *sc = (struct ath_softc *) common->priv;
+ struct ath_softc *sc = common->priv;
if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) {
unsigned long flags;
@@ -132,9 +186,9 @@ static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
{
- struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_hw *ah = hw_priv;
struct ath_common *common = ath9k_hw_common(ah);
- struct ath_softc *sc = (struct ath_softc *) common->priv;
+ struct ath_softc *sc = common->priv;
u32 val;
if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) {
@@ -172,10 +226,10 @@ static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset,
static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
{
- struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_hw *ah = hw_priv;
struct ath_common *common = ath9k_hw_common(ah);
- struct ath_softc *sc = (struct ath_softc *) common->priv;
- unsigned long uninitialized_var(flags);
+ struct ath_softc *sc = common->priv;
+ unsigned long flags;
u32 val;
if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_ON) {
@@ -202,6 +256,11 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
ath_reg_notifier_apply(wiphy, request, reg);
+ /* synchronize DFS detector if regulatory domain changed */
+ if (sc->dfs_detector != NULL)
+ sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
+ request->dfs_region);
+
/* Set tx power */
if (!ah->curchan)
return;
@@ -212,10 +271,6 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
sc->cur_chan->txpower,
&sc->cur_chan->cur_txpower);
- /* synchronize DFS detector if regulatory domain changed */
- if (sc->dfs_detector != NULL)
- sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
- request->dfs_region);
ath9k_ps_restore(sc);
}
@@ -275,7 +330,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
if (!dd->dd_desc)
return -ENOMEM;
- ds = (u8 *) dd->dd_desc;
+ ds = dd->dd_desc;
ath_dbg(common, CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
name, ds, (u32) dd->dd_desc_len,
ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
@@ -369,10 +424,10 @@ static void ath9k_init_misc(struct ath_softc *sc)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int i = 0;
- setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
+ timer_setup(&common->ani.timer, ath_ani_calibrate, 0);
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
- memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+ eth_broadcast_addr(common->bssidmask);
sc->beacon.slottime = 9;
for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++)
@@ -513,45 +568,53 @@ static void ath9k_eeprom_release(struct ath_softc *sc)
release_firmware(sc->sc_ah->eeprom_blob);
}
-static int ath9k_init_platform(struct ath_softc *sc)
+static int ath9k_nvmem_request_eeprom(struct ath_softc *sc)
{
- struct ath9k_platform_data *pdata = sc->dev->platform_data;
struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- int ret;
+ struct nvmem_cell *cell;
+ void *buf;
+ size_t len;
+ int err;
- if (!pdata)
- return 0;
+ cell = devm_nvmem_cell_get(sc->dev, "calibration");
+ if (IS_ERR(cell)) {
+ err = PTR_ERR(cell);
- if (!pdata->use_eeprom) {
- ah->ah_flags &= ~AH_USE_EEPROM;
- ah->gpio_mask = pdata->gpio_mask;
- ah->gpio_val = pdata->gpio_val;
- ah->led_pin = pdata->led_pin;
- ah->is_clk_25mhz = pdata->is_clk_25mhz;
- ah->get_mac_revision = pdata->get_mac_revision;
- ah->external_reset = pdata->external_reset;
- ah->disable_2ghz = pdata->disable_2ghz;
- ah->disable_5ghz = pdata->disable_5ghz;
-
- if (!pdata->endian_check)
- ah->ah_flags |= AH_NO_EEP_SWAP;
- }
+ /* nvmem cell might not be defined, or the nvmem
+ * subsystem isn't included. In this case, follow
+ * the established "just return 0;" convention
+ * to say:
+ * "All good. Nothing to see here. Please go on."
+ */
+ if (err == -ENOENT || err == -EOPNOTSUPP)
+ return 0;
- if (pdata->eeprom_name) {
- ret = ath9k_eeprom_request(sc, pdata->eeprom_name);
- if (ret)
- return ret;
+ return err;
}
- if (pdata->led_active_high)
- ah->config.led_active_high = true;
+ buf = nvmem_cell_read(cell, &len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* run basic sanity checks on the returned nvram cell length.
+ * That length has to be a multiple of a "u16" (i.e.: & 1).
+ * Furthermore, it has to be more than "let's say" 512 bytes
+ * but less than the maximum of AR9300_EEPROM_SIZE (16kb).
+ */
+ if ((len & 1) == 1 || len < 512 || len >= AR9300_EEPROM_SIZE) {
+ kfree(buf);
+ return -EINVAL;
+ }
- if (pdata->tx_gain_buffalo)
- ah->config.tx_gain_buffalo = true;
+ /* devres manages the calibration values release on shutdown */
+ ah->nvmem_blob = devm_kmemdup(sc->dev, buf, len, GFP_KERNEL);
+ kfree(buf);
+ if (!ah->nvmem_blob)
+ return -ENOMEM;
- if (pdata->macaddr)
- ether_addr_copy(common->macaddr, pdata->macaddr);
+ ah->nvmem_blob_len = len;
+ ah->ah_flags &= ~AH_USE_EEPROM;
+ ah->ah_flags |= AH_NO_EEP_SWAP;
return 0;
}
@@ -562,7 +625,6 @@ static int ath9k_of_init(struct ath_softc *sc)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
enum ath_bus_type bus_type = common->bus_ops->ath_bus_type;
- const char *mac;
char eeprom_name[100];
int ret;
@@ -580,14 +642,14 @@ static int ath9k_of_init(struct ath_softc *sc)
ret = ath9k_eeprom_request(sc, eeprom_name);
if (ret)
return ret;
- }
- mac = of_get_mac_address(np);
- if (mac)
- ether_addr_copy(common->macaddr, mac);
+ ah->ah_flags &= ~AH_USE_EEPROM;
+ ah->ah_flags |= AH_NO_EEP_SWAP;
+ }
- ah->ah_flags &= ~AH_USE_EEPROM;
- ah->ah_flags |= AH_NO_EEP_SWAP;
+ ret = of_get_mac_address(np, common->macaddr);
+ if (ret == -EPROBE_DEFER)
+ return ret;
return 0;
}
@@ -620,8 +682,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
/* Will be cleared in ath9k_start() */
set_bit(ATH_OP_INVALID, &common->op_flags);
- sc->airtime_flags = (AIRTIME_USE_TX | AIRTIME_USE_RX |
- AIRTIME_USE_NEW_QUEUES);
sc->sc_ah = ah;
sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
@@ -646,11 +706,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
*/
ath9k_init_pcoem_platform(sc);
- ret = ath9k_init_platform(sc);
+ ret = ath9k_of_init(sc);
if (ret)
return ret;
- ret = ath9k_of_init(sc);
+ ret = ath9k_nvmem_request_eeprom(sc);
if (ret)
return ret;
@@ -674,11 +734,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&sc->sc_pm_lock);
spin_lock_init(&sc->chan_lock);
mutex_init(&sc->mutex);
- tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
- tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
- (unsigned long)sc);
+ tasklet_setup(&sc->intr_tq, ath9k_tasklet);
+ tasklet_setup(&sc->bcon_tasklet, ath9k_beacon_tasklet);
- setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
+ timer_setup(&sc->sleep_timer, ath_ps_full_sleep, 0);
INIT_WORK(&sc->hw_reset_work, ath_reset_work);
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
@@ -751,7 +810,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
ah->curchan = &ah->channels[chan->hw_value];
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
ath9k_cmn_get_channel(sc->hw, ah, &chandef);
- ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
+ ath9k_hw_set_txpowerlimit(ah, MAX_COMBINED_POWER, true);
}
}
@@ -779,12 +838,6 @@ static const struct ieee80211_iface_limit if_limits[] = {
BIT(NL80211_IFTYPE_P2P_GO) },
};
-#ifdef CONFIG_WIRELESS_WDS
-static const struct ieee80211_iface_limit wds_limits[] = {
- { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) },
-};
-#endif
-
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
static const struct ieee80211_iface_limit if_limits_multi[] = {
@@ -821,15 +874,6 @@ static const struct ieee80211_iface_combination if_comb[] = {
BIT(NL80211_CHAN_WIDTH_40),
#endif
},
-#ifdef CONFIG_WIRELESS_WDS
- {
- .limits = wds_limits,
- .n_limits = ARRAY_SIZE(wds_limits),
- .max_interfaces = 2048,
- .num_different_channels = 1,
- .beacon_int_infra_match = true,
- },
-#endif
};
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
@@ -844,7 +888,6 @@ static void ath9k_set_mcc_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, QUEUE_CONTROL);
hw->queues = ATH9K_NUM_TX_QUEUES;
hw->offchannel_tx_hw_queue = hw->queues - 1;
- hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
hw->wiphy->iface_combinations = if_comb_multi;
hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
hw->wiphy->max_scan_ssids = 255;
@@ -900,9 +943,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT) |
-#ifdef CONFIG_WIRELESS_WDS
- BIT(NL80211_IFTYPE_WDS) |
-#endif
BIT(NL80211_IFTYPE_OCB);
if (ath9k_is_chanctx_enabled())
@@ -957,6 +997,10 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS);
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
}
int ath9k_init_device(u16 devid, struct ath_softc *sc,
@@ -1004,6 +1048,8 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
ARRAY_SIZE(ath9k_tpt_blink));
#endif
+ wiphy_read_of_freq_limits(hw->wiphy);
+
/* Register with mac80211 */
error = ieee80211_register_hw(hw);
if (error)
@@ -1053,7 +1099,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
- del_timer_sync(&sc->sleep_timer);
+ timer_delete_sync(&sc->sleep_timer);
ath9k_hw_deinit(sc->sc_ah);
if (sc->dfs_detector != NULL)
sc->dfs_detector->exit(sc->dfs_detector);
@@ -1100,6 +1146,8 @@ static int __init ath9k_init(void)
goto err_pci_exit;
}
+ dmi_check_system(ath9k_quirks);
+
return 0;
err_pci_exit: