1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2a910e4a9SSolomon Peachy /* 3a910e4a9SSolomon Peachy * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers 4a910e4a9SSolomon Peachy * 5a910e4a9SSolomon Peachy * Copyright (c) 2010, ST-Ericsson 6a910e4a9SSolomon Peachy * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 7a910e4a9SSolomon Peachy * 8a910e4a9SSolomon Peachy * Based on: 9a910e4a9SSolomon Peachy * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 10a910e4a9SSolomon Peachy * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> 11a910e4a9SSolomon Peachy * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 12a910e4a9SSolomon Peachy * 13a910e4a9SSolomon Peachy * Based on: 14a910e4a9SSolomon Peachy * - the islsm (softmac prism54) driver, which is: 15a910e4a9SSolomon Peachy * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. 16a910e4a9SSolomon Peachy * - stlc45xx driver 17a910e4a9SSolomon Peachy * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). 18a910e4a9SSolomon Peachy */ 19a910e4a9SSolomon Peachy 20a910e4a9SSolomon Peachy #include <linux/module.h> 21a910e4a9SSolomon Peachy #include <linux/firmware.h> 22a910e4a9SSolomon Peachy #include <linux/etherdevice.h> 23a910e4a9SSolomon Peachy #include <linux/vmalloc.h> 24a910e4a9SSolomon Peachy #include <linux/random.h> 25a910e4a9SSolomon Peachy #include <linux/sched.h> 26a910e4a9SSolomon Peachy #include <net/mac80211.h> 27a910e4a9SSolomon Peachy 28a910e4a9SSolomon Peachy #include "cw1200.h" 29a910e4a9SSolomon Peachy #include "txrx.h" 30911373ccSSolomon Peachy #include "hwbus.h" 31a910e4a9SSolomon Peachy #include "fwio.h" 32a910e4a9SSolomon Peachy #include "hwio.h" 33a910e4a9SSolomon Peachy #include "bh.h" 34a910e4a9SSolomon Peachy #include "sta.h" 35a910e4a9SSolomon Peachy #include "scan.h" 36a910e4a9SSolomon Peachy #include "debug.h" 37a910e4a9SSolomon Peachy #include "pm.h" 38a910e4a9SSolomon Peachy 39a910e4a9SSolomon Peachy MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>"); 40a910e4a9SSolomon Peachy MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); 41a910e4a9SSolomon Peachy MODULE_LICENSE("GPL"); 42a910e4a9SSolomon Peachy MODULE_ALIAS("cw1200_core"); 43a910e4a9SSolomon Peachy 44a910e4a9SSolomon Peachy /* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ 45a910e4a9SSolomon Peachy static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00}; 462ef00c53SJoe Perches module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, 0444); 47a910e4a9SSolomon Peachy MODULE_PARM_DESC(macaddr, "Override platform_data MAC address"); 48a910e4a9SSolomon Peachy 49a910e4a9SSolomon Peachy static char *cw1200_sdd_path; 50a910e4a9SSolomon Peachy module_param(cw1200_sdd_path, charp, 0644); 51a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file"); 52a910e4a9SSolomon Peachy static int cw1200_refclk; 53a910e4a9SSolomon Peachy module_param(cw1200_refclk, int, 0644); 54a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock"); 55a910e4a9SSolomon Peachy 56a910e4a9SSolomon Peachy int cw1200_power_mode = wsm_power_mode_quiescent; 57a910e4a9SSolomon Peachy module_param(cw1200_power_mode, int, 0644); 58a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); 59a910e4a9SSolomon Peachy 60a910e4a9SSolomon Peachy #define RATETAB_ENT(_rate, _rateid, _flags) \ 61a910e4a9SSolomon Peachy { \ 62a910e4a9SSolomon Peachy .bitrate = (_rate), \ 63a910e4a9SSolomon Peachy .hw_value = (_rateid), \ 64a910e4a9SSolomon Peachy .flags = (_flags), \ 65a910e4a9SSolomon Peachy } 66a910e4a9SSolomon Peachy 67a910e4a9SSolomon Peachy static struct ieee80211_rate cw1200_rates[] = { 68a910e4a9SSolomon Peachy RATETAB_ENT(10, 0, 0), 69a910e4a9SSolomon Peachy RATETAB_ENT(20, 1, 0), 70a910e4a9SSolomon Peachy RATETAB_ENT(55, 2, 0), 71a910e4a9SSolomon Peachy RATETAB_ENT(110, 3, 0), 72a910e4a9SSolomon Peachy RATETAB_ENT(60, 6, 0), 73a910e4a9SSolomon Peachy RATETAB_ENT(90, 7, 0), 74a910e4a9SSolomon Peachy RATETAB_ENT(120, 8, 0), 75a910e4a9SSolomon Peachy RATETAB_ENT(180, 9, 0), 76a910e4a9SSolomon Peachy RATETAB_ENT(240, 10, 0), 77a910e4a9SSolomon Peachy RATETAB_ENT(360, 11, 0), 78a910e4a9SSolomon Peachy RATETAB_ENT(480, 12, 0), 79a910e4a9SSolomon Peachy RATETAB_ENT(540, 13, 0), 80a910e4a9SSolomon Peachy }; 81a910e4a9SSolomon Peachy 82a910e4a9SSolomon Peachy static struct ieee80211_rate cw1200_mcs_rates[] = { 83a910e4a9SSolomon Peachy RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), 84a910e4a9SSolomon Peachy RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), 85a910e4a9SSolomon Peachy RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), 86a910e4a9SSolomon Peachy RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), 87a910e4a9SSolomon Peachy RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), 88a910e4a9SSolomon Peachy RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), 89a910e4a9SSolomon Peachy RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), 90a910e4a9SSolomon Peachy RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), 91a910e4a9SSolomon Peachy }; 92a910e4a9SSolomon Peachy 93a910e4a9SSolomon Peachy #define cw1200_a_rates (cw1200_rates + 4) 94a910e4a9SSolomon Peachy #define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) 95a910e4a9SSolomon Peachy #define cw1200_g_rates (cw1200_rates + 0) 96a910e4a9SSolomon Peachy #define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) 97a910e4a9SSolomon Peachy #define cw1200_n_rates (cw1200_mcs_rates) 98a910e4a9SSolomon Peachy #define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) 99a910e4a9SSolomon Peachy 100a910e4a9SSolomon Peachy 101a910e4a9SSolomon Peachy #define CHAN2G(_channel, _freq, _flags) { \ 10257fbcce3SJohannes Berg .band = NL80211_BAND_2GHZ, \ 103a910e4a9SSolomon Peachy .center_freq = (_freq), \ 104a910e4a9SSolomon Peachy .hw_value = (_channel), \ 105a910e4a9SSolomon Peachy .flags = (_flags), \ 106a910e4a9SSolomon Peachy .max_antenna_gain = 0, \ 107a910e4a9SSolomon Peachy .max_power = 30, \ 108a910e4a9SSolomon Peachy } 109a910e4a9SSolomon Peachy 110a910e4a9SSolomon Peachy #define CHAN5G(_channel, _flags) { \ 11157fbcce3SJohannes Berg .band = NL80211_BAND_5GHZ, \ 112a910e4a9SSolomon Peachy .center_freq = 5000 + (5 * (_channel)), \ 113a910e4a9SSolomon Peachy .hw_value = (_channel), \ 114a910e4a9SSolomon Peachy .flags = (_flags), \ 115a910e4a9SSolomon Peachy .max_antenna_gain = 0, \ 116a910e4a9SSolomon Peachy .max_power = 30, \ 117a910e4a9SSolomon Peachy } 118a910e4a9SSolomon Peachy 119a910e4a9SSolomon Peachy static struct ieee80211_channel cw1200_2ghz_chantable[] = { 120a910e4a9SSolomon Peachy CHAN2G(1, 2412, 0), 121a910e4a9SSolomon Peachy CHAN2G(2, 2417, 0), 122a910e4a9SSolomon Peachy CHAN2G(3, 2422, 0), 123a910e4a9SSolomon Peachy CHAN2G(4, 2427, 0), 124a910e4a9SSolomon Peachy CHAN2G(5, 2432, 0), 125a910e4a9SSolomon Peachy CHAN2G(6, 2437, 0), 126a910e4a9SSolomon Peachy CHAN2G(7, 2442, 0), 127a910e4a9SSolomon Peachy CHAN2G(8, 2447, 0), 128a910e4a9SSolomon Peachy CHAN2G(9, 2452, 0), 129a910e4a9SSolomon Peachy CHAN2G(10, 2457, 0), 130a910e4a9SSolomon Peachy CHAN2G(11, 2462, 0), 131a910e4a9SSolomon Peachy CHAN2G(12, 2467, 0), 132a910e4a9SSolomon Peachy CHAN2G(13, 2472, 0), 133a910e4a9SSolomon Peachy CHAN2G(14, 2484, 0), 134a910e4a9SSolomon Peachy }; 135a910e4a9SSolomon Peachy 136a910e4a9SSolomon Peachy static struct ieee80211_channel cw1200_5ghz_chantable[] = { 137a910e4a9SSolomon Peachy CHAN5G(34, 0), CHAN5G(36, 0), 138a910e4a9SSolomon Peachy CHAN5G(38, 0), CHAN5G(40, 0), 139a910e4a9SSolomon Peachy CHAN5G(42, 0), CHAN5G(44, 0), 140a910e4a9SSolomon Peachy CHAN5G(46, 0), CHAN5G(48, 0), 141a910e4a9SSolomon Peachy CHAN5G(52, 0), CHAN5G(56, 0), 142a910e4a9SSolomon Peachy CHAN5G(60, 0), CHAN5G(64, 0), 143a910e4a9SSolomon Peachy CHAN5G(100, 0), CHAN5G(104, 0), 144a910e4a9SSolomon Peachy CHAN5G(108, 0), CHAN5G(112, 0), 145a910e4a9SSolomon Peachy CHAN5G(116, 0), CHAN5G(120, 0), 146a910e4a9SSolomon Peachy CHAN5G(124, 0), CHAN5G(128, 0), 147a910e4a9SSolomon Peachy CHAN5G(132, 0), CHAN5G(136, 0), 148a910e4a9SSolomon Peachy CHAN5G(140, 0), CHAN5G(149, 0), 149a910e4a9SSolomon Peachy CHAN5G(153, 0), CHAN5G(157, 0), 150a910e4a9SSolomon Peachy CHAN5G(161, 0), CHAN5G(165, 0), 151a910e4a9SSolomon Peachy CHAN5G(184, 0), CHAN5G(188, 0), 152a910e4a9SSolomon Peachy CHAN5G(192, 0), CHAN5G(196, 0), 153a910e4a9SSolomon Peachy CHAN5G(200, 0), CHAN5G(204, 0), 154a910e4a9SSolomon Peachy CHAN5G(208, 0), CHAN5G(212, 0), 155a910e4a9SSolomon Peachy CHAN5G(216, 0), 156a910e4a9SSolomon Peachy }; 157a910e4a9SSolomon Peachy 158a910e4a9SSolomon Peachy static struct ieee80211_supported_band cw1200_band_2ghz = { 159a910e4a9SSolomon Peachy .channels = cw1200_2ghz_chantable, 160a910e4a9SSolomon Peachy .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), 161a910e4a9SSolomon Peachy .bitrates = cw1200_g_rates, 162a910e4a9SSolomon Peachy .n_bitrates = cw1200_g_rates_size, 163a910e4a9SSolomon Peachy .ht_cap = { 164a910e4a9SSolomon Peachy .cap = IEEE80211_HT_CAP_GRN_FLD | 165a910e4a9SSolomon Peachy (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | 166a910e4a9SSolomon Peachy IEEE80211_HT_CAP_MAX_AMSDU, 167a910e4a9SSolomon Peachy .ht_supported = 1, 168a910e4a9SSolomon Peachy .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, 169a910e4a9SSolomon Peachy .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, 170a910e4a9SSolomon Peachy .mcs = { 171a910e4a9SSolomon Peachy .rx_mask[0] = 0xFF, 172a910e4a9SSolomon Peachy .rx_highest = __cpu_to_le16(0x41), 173a910e4a9SSolomon Peachy .tx_params = IEEE80211_HT_MCS_TX_DEFINED, 174a910e4a9SSolomon Peachy }, 175a910e4a9SSolomon Peachy }, 176a910e4a9SSolomon Peachy }; 177a910e4a9SSolomon Peachy 178a910e4a9SSolomon Peachy static struct ieee80211_supported_band cw1200_band_5ghz = { 179a910e4a9SSolomon Peachy .channels = cw1200_5ghz_chantable, 180a910e4a9SSolomon Peachy .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), 181a910e4a9SSolomon Peachy .bitrates = cw1200_a_rates, 182a910e4a9SSolomon Peachy .n_bitrates = cw1200_a_rates_size, 183a910e4a9SSolomon Peachy .ht_cap = { 184a910e4a9SSolomon Peachy .cap = IEEE80211_HT_CAP_GRN_FLD | 185a910e4a9SSolomon Peachy (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | 186a910e4a9SSolomon Peachy IEEE80211_HT_CAP_MAX_AMSDU, 187a910e4a9SSolomon Peachy .ht_supported = 1, 188a910e4a9SSolomon Peachy .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, 189a910e4a9SSolomon Peachy .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, 190a910e4a9SSolomon Peachy .mcs = { 191a910e4a9SSolomon Peachy .rx_mask[0] = 0xFF, 192a910e4a9SSolomon Peachy .rx_highest = __cpu_to_le16(0x41), 193a910e4a9SSolomon Peachy .tx_params = IEEE80211_HT_MCS_TX_DEFINED, 194a910e4a9SSolomon Peachy }, 195a910e4a9SSolomon Peachy }, 196a910e4a9SSolomon Peachy }; 197a910e4a9SSolomon Peachy 198a910e4a9SSolomon Peachy static const unsigned long cw1200_ttl[] = { 199a910e4a9SSolomon Peachy 1 * HZ, /* VO */ 200a910e4a9SSolomon Peachy 2 * HZ, /* VI */ 201a910e4a9SSolomon Peachy 5 * HZ, /* BE */ 202a910e4a9SSolomon Peachy 10 * HZ /* BK */ 203a910e4a9SSolomon Peachy }; 204a910e4a9SSolomon Peachy 205a910e4a9SSolomon Peachy static const struct ieee80211_ops cw1200_ops = { 206a910e4a9SSolomon Peachy .start = cw1200_start, 207a910e4a9SSolomon Peachy .stop = cw1200_stop, 208a910e4a9SSolomon Peachy .add_interface = cw1200_add_interface, 209a910e4a9SSolomon Peachy .remove_interface = cw1200_remove_interface, 210a910e4a9SSolomon Peachy .change_interface = cw1200_change_interface, 211a910e4a9SSolomon Peachy .tx = cw1200_tx, 212a910e4a9SSolomon Peachy .hw_scan = cw1200_hw_scan, 213a910e4a9SSolomon Peachy .set_tim = cw1200_set_tim, 214a910e4a9SSolomon Peachy .sta_notify = cw1200_sta_notify, 215a910e4a9SSolomon Peachy .sta_add = cw1200_sta_add, 216a910e4a9SSolomon Peachy .sta_remove = cw1200_sta_remove, 217a910e4a9SSolomon Peachy .set_key = cw1200_set_key, 218a910e4a9SSolomon Peachy .set_rts_threshold = cw1200_set_rts_threshold, 219a910e4a9SSolomon Peachy .config = cw1200_config, 220a910e4a9SSolomon Peachy .bss_info_changed = cw1200_bss_info_changed, 221a910e4a9SSolomon Peachy .prepare_multicast = cw1200_prepare_multicast, 222a910e4a9SSolomon Peachy .configure_filter = cw1200_configure_filter, 223a910e4a9SSolomon Peachy .conf_tx = cw1200_conf_tx, 224a910e4a9SSolomon Peachy .get_stats = cw1200_get_stats, 225a910e4a9SSolomon Peachy .ampdu_action = cw1200_ampdu_action, 226a910e4a9SSolomon Peachy .flush = cw1200_flush, 2274e17b87eSSolomon Peachy #ifdef CONFIG_PM 228a910e4a9SSolomon Peachy .suspend = cw1200_wow_suspend, 229a910e4a9SSolomon Peachy .resume = cw1200_wow_resume, 2304e17b87eSSolomon Peachy #endif 231a910e4a9SSolomon Peachy /* Intentionally not offloaded: */ 232a910e4a9SSolomon Peachy /*.channel_switch = cw1200_channel_switch, */ 233a910e4a9SSolomon Peachy /*.remain_on_channel = cw1200_remain_on_channel, */ 234a910e4a9SSolomon Peachy /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ 235a910e4a9SSolomon Peachy }; 236a910e4a9SSolomon Peachy 2377258416cSSolomon Peachy static int cw1200_ba_rx_tids = -1; 2387258416cSSolomon Peachy static int cw1200_ba_tx_tids = -1; 239a910e4a9SSolomon Peachy module_param(cw1200_ba_rx_tids, int, 0644); 240a910e4a9SSolomon Peachy module_param(cw1200_ba_tx_tids, int, 0644); 241a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs"); 242a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs"); 243a910e4a9SSolomon Peachy 24451217ceeSJohannes Berg #ifdef CONFIG_PM 24551217ceeSJohannes Berg static const struct wiphy_wowlan_support cw1200_wowlan_support = { 24651217ceeSJohannes Berg /* Support only for limited wowlan functionalities */ 24751217ceeSJohannes Berg .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, 24851217ceeSJohannes Berg }; 24951217ceeSJohannes Berg #endif 25051217ceeSJohannes Berg 25151217ceeSJohannes Berg 252a910e4a9SSolomon Peachy static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, 253a910e4a9SSolomon Peachy const bool have_5ghz) 254a910e4a9SSolomon Peachy { 255a910e4a9SSolomon Peachy int i, band; 256a910e4a9SSolomon Peachy struct ieee80211_hw *hw; 257a910e4a9SSolomon Peachy struct cw1200_common *priv; 258a910e4a9SSolomon Peachy 259a910e4a9SSolomon Peachy hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops); 260a910e4a9SSolomon Peachy if (!hw) 261a910e4a9SSolomon Peachy return NULL; 262a910e4a9SSolomon Peachy 263a910e4a9SSolomon Peachy priv = hw->priv; 264a910e4a9SSolomon Peachy priv->hw = hw; 265a910e4a9SSolomon Peachy priv->hw_type = -1; 266a910e4a9SSolomon Peachy priv->mode = NL80211_IFTYPE_UNSPECIFIED; 267a910e4a9SSolomon Peachy priv->rates = cw1200_rates; /* TODO: fetch from FW */ 268a910e4a9SSolomon Peachy priv->mcs_rates = cw1200_n_rates; 269a910e4a9SSolomon Peachy if (cw1200_ba_rx_tids != -1) 270a910e4a9SSolomon Peachy priv->ba_rx_tid_mask = cw1200_ba_rx_tids; 271a910e4a9SSolomon Peachy else 272a910e4a9SSolomon Peachy priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */ 273a910e4a9SSolomon Peachy if (cw1200_ba_tx_tids != -1) 274a910e4a9SSolomon Peachy priv->ba_tx_tid_mask = cw1200_ba_tx_tids; 275a910e4a9SSolomon Peachy else 276a910e4a9SSolomon Peachy priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */ 277a910e4a9SSolomon Peachy 27830686bf7SJohannes Berg ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); 27930686bf7SJohannes Berg ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); 28030686bf7SJohannes Berg ieee80211_hw_set(hw, AMPDU_AGGREGATION); 28130686bf7SJohannes Berg ieee80211_hw_set(hw, CONNECTION_MONITOR); 28230686bf7SJohannes Berg ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); 28330686bf7SJohannes Berg ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); 28430686bf7SJohannes Berg ieee80211_hw_set(hw, SIGNAL_DBM); 28530686bf7SJohannes Berg ieee80211_hw_set(hw, SUPPORTS_PS); 286a910e4a9SSolomon Peachy 287a910e4a9SSolomon Peachy hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 288a910e4a9SSolomon Peachy BIT(NL80211_IFTYPE_ADHOC) | 289a910e4a9SSolomon Peachy BIT(NL80211_IFTYPE_AP) | 290a910e4a9SSolomon Peachy BIT(NL80211_IFTYPE_MESH_POINT) | 291a910e4a9SSolomon Peachy BIT(NL80211_IFTYPE_P2P_CLIENT) | 292a910e4a9SSolomon Peachy BIT(NL80211_IFTYPE_P2P_GO); 293a910e4a9SSolomon Peachy 2944e17b87eSSolomon Peachy #ifdef CONFIG_PM 29551217ceeSJohannes Berg hw->wiphy->wowlan = &cw1200_wowlan_support; 2964e17b87eSSolomon Peachy #endif 297a910e4a9SSolomon Peachy 298a910e4a9SSolomon Peachy hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; 299a910e4a9SSolomon Peachy 300a910e4a9SSolomon Peachy hw->queues = 4; 301a910e4a9SSolomon Peachy 302a910e4a9SSolomon Peachy priv->rts_threshold = -1; 303a910e4a9SSolomon Peachy 304a910e4a9SSolomon Peachy hw->max_rates = 8; 305a910e4a9SSolomon Peachy hw->max_rate_tries = 15; 306a910e4a9SSolomon Peachy hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + 307a910e4a9SSolomon Peachy 8; /* TKIP IV */ 308a910e4a9SSolomon Peachy 309a910e4a9SSolomon Peachy hw->sta_data_size = sizeof(struct cw1200_sta_priv); 310a910e4a9SSolomon Peachy 31157fbcce3SJohannes Berg hw->wiphy->bands[NL80211_BAND_2GHZ] = &cw1200_band_2ghz; 312a910e4a9SSolomon Peachy if (have_5ghz) 31357fbcce3SJohannes Berg hw->wiphy->bands[NL80211_BAND_5GHZ] = &cw1200_band_5ghz; 314a910e4a9SSolomon Peachy 315a910e4a9SSolomon Peachy /* Channel params have to be cleared before registering wiphy again */ 31657fbcce3SJohannes Berg for (band = 0; band < NUM_NL80211_BANDS; band++) { 317a910e4a9SSolomon Peachy struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; 318a910e4a9SSolomon Peachy if (!sband) 319a910e4a9SSolomon Peachy continue; 320a910e4a9SSolomon Peachy for (i = 0; i < sband->n_channels; i++) { 321a910e4a9SSolomon Peachy sband->channels[i].flags = 0; 322a910e4a9SSolomon Peachy sband->channels[i].max_antenna_gain = 0; 323a910e4a9SSolomon Peachy sband->channels[i].max_power = 30; 324a910e4a9SSolomon Peachy } 325a910e4a9SSolomon Peachy } 326a910e4a9SSolomon Peachy 327a910e4a9SSolomon Peachy hw->wiphy->max_scan_ssids = 2; 328a910e4a9SSolomon Peachy hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; 329a910e4a9SSolomon Peachy 330a910e4a9SSolomon Peachy if (macaddr) 331a910e4a9SSolomon Peachy SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr); 332a910e4a9SSolomon Peachy else 333a910e4a9SSolomon Peachy SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); 334a910e4a9SSolomon Peachy 335a910e4a9SSolomon Peachy /* Fix up mac address if necessary */ 336a910e4a9SSolomon Peachy if (hw->wiphy->perm_addr[3] == 0 && 337a910e4a9SSolomon Peachy hw->wiphy->perm_addr[4] == 0 && 338a910e4a9SSolomon Peachy hw->wiphy->perm_addr[5] == 0) { 339a910e4a9SSolomon Peachy get_random_bytes(&hw->wiphy->perm_addr[3], 3); 340a910e4a9SSolomon Peachy } 341a910e4a9SSolomon Peachy 342a910e4a9SSolomon Peachy mutex_init(&priv->wsm_cmd_mux); 343a910e4a9SSolomon Peachy mutex_init(&priv->conf_mutex); 344a910e4a9SSolomon Peachy priv->workqueue = create_singlethread_workqueue("cw1200_wq"); 3450ed2a005SKangjie Lu if (!priv->workqueue) { 3460ed2a005SKangjie Lu ieee80211_free_hw(hw); 3470ed2a005SKangjie Lu return NULL; 3480ed2a005SKangjie Lu } 3490ed2a005SKangjie Lu 350a910e4a9SSolomon Peachy sema_init(&priv->scan.lock, 1); 351a910e4a9SSolomon Peachy INIT_WORK(&priv->scan.work, cw1200_scan_work); 352a910e4a9SSolomon Peachy INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); 353a910e4a9SSolomon Peachy INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); 354a910e4a9SSolomon Peachy INIT_DELAYED_WORK(&priv->clear_recent_scan_work, 355a910e4a9SSolomon Peachy cw1200_clear_recent_scan_work); 356a910e4a9SSolomon Peachy INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); 357a910e4a9SSolomon Peachy INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); 358a910e4a9SSolomon Peachy INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work); 359a910e4a9SSolomon Peachy INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); 360a910e4a9SSolomon Peachy INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); 361a910e4a9SSolomon Peachy spin_lock_init(&priv->event_queue_lock); 362a910e4a9SSolomon Peachy INIT_LIST_HEAD(&priv->event_queue); 363a910e4a9SSolomon Peachy INIT_WORK(&priv->event_handler, cw1200_event_handler); 364a910e4a9SSolomon Peachy INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); 365a910e4a9SSolomon Peachy INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work); 366a910e4a9SSolomon Peachy spin_lock_init(&priv->bss_loss_lock); 367a910e4a9SSolomon Peachy spin_lock_init(&priv->ps_state_lock); 368a910e4a9SSolomon Peachy INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work); 369a910e4a9SSolomon Peachy INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); 370a910e4a9SSolomon Peachy INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); 371a910e4a9SSolomon Peachy INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); 372a910e4a9SSolomon Peachy INIT_WORK(&priv->link_id_work, cw1200_link_id_work); 373a910e4a9SSolomon Peachy INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); 374a910e4a9SSolomon Peachy INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); 375a910e4a9SSolomon Peachy INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); 376a910e4a9SSolomon Peachy INIT_WORK(&priv->set_beacon_wakeup_period_work, 377a910e4a9SSolomon Peachy cw1200_set_beacon_wakeup_period_work); 378e3dcf8bbSKees Cook timer_setup(&priv->mcast_timeout, cw1200_mcast_timeout, 0); 379a910e4a9SSolomon Peachy 380a910e4a9SSolomon Peachy if (cw1200_queue_stats_init(&priv->tx_queue_stats, 381a910e4a9SSolomon Peachy CW1200_LINK_ID_MAX, 382a910e4a9SSolomon Peachy cw1200_skb_dtor, 383a910e4a9SSolomon Peachy priv)) { 384a910e4a9SSolomon Peachy ieee80211_free_hw(hw); 385a910e4a9SSolomon Peachy return NULL; 386a910e4a9SSolomon Peachy } 387a910e4a9SSolomon Peachy 388a910e4a9SSolomon Peachy for (i = 0; i < 4; ++i) { 389a910e4a9SSolomon Peachy if (cw1200_queue_init(&priv->tx_queue[i], 390a910e4a9SSolomon Peachy &priv->tx_queue_stats, i, 16, 391a910e4a9SSolomon Peachy cw1200_ttl[i])) { 392a910e4a9SSolomon Peachy for (; i > 0; i--) 393a910e4a9SSolomon Peachy cw1200_queue_deinit(&priv->tx_queue[i - 1]); 394a910e4a9SSolomon Peachy cw1200_queue_stats_deinit(&priv->tx_queue_stats); 395a910e4a9SSolomon Peachy ieee80211_free_hw(hw); 396a910e4a9SSolomon Peachy return NULL; 397a910e4a9SSolomon Peachy } 398a910e4a9SSolomon Peachy } 399a910e4a9SSolomon Peachy 400a910e4a9SSolomon Peachy init_waitqueue_head(&priv->channel_switch_done); 401a910e4a9SSolomon Peachy init_waitqueue_head(&priv->wsm_cmd_wq); 402a910e4a9SSolomon Peachy init_waitqueue_head(&priv->wsm_startup_done); 403a910e4a9SSolomon Peachy init_waitqueue_head(&priv->ps_mode_switch_done); 404a910e4a9SSolomon Peachy wsm_buf_init(&priv->wsm_cmd_buf); 405a910e4a9SSolomon Peachy spin_lock_init(&priv->wsm_cmd.lock); 406a910e4a9SSolomon Peachy priv->wsm_cmd.done = 1; 407a910e4a9SSolomon Peachy tx_policy_init(priv); 408a910e4a9SSolomon Peachy 409a910e4a9SSolomon Peachy return hw; 410a910e4a9SSolomon Peachy } 411a910e4a9SSolomon Peachy 412a910e4a9SSolomon Peachy static int cw1200_register_common(struct ieee80211_hw *dev) 413a910e4a9SSolomon Peachy { 414a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 415a910e4a9SSolomon Peachy int err; 416a910e4a9SSolomon Peachy 4174e17b87eSSolomon Peachy #ifdef CONFIG_PM 418a910e4a9SSolomon Peachy err = cw1200_pm_init(&priv->pm_state, priv); 419a910e4a9SSolomon Peachy if (err) { 420a910e4a9SSolomon Peachy pr_err("Cannot init PM. (%d).\n", 421a910e4a9SSolomon Peachy err); 422a910e4a9SSolomon Peachy return err; 423a910e4a9SSolomon Peachy } 4244e17b87eSSolomon Peachy #endif 425a910e4a9SSolomon Peachy 426a910e4a9SSolomon Peachy err = ieee80211_register_hw(dev); 427a910e4a9SSolomon Peachy if (err) { 428a910e4a9SSolomon Peachy pr_err("Cannot register device (%d).\n", 429a910e4a9SSolomon Peachy err); 4304e17b87eSSolomon Peachy #ifdef CONFIG_PM 431a910e4a9SSolomon Peachy cw1200_pm_deinit(&priv->pm_state); 4324e17b87eSSolomon Peachy #endif 433a910e4a9SSolomon Peachy return err; 434a910e4a9SSolomon Peachy } 435a910e4a9SSolomon Peachy 436a910e4a9SSolomon Peachy cw1200_debug_init(priv); 437a910e4a9SSolomon Peachy 438a910e4a9SSolomon Peachy pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); 439a910e4a9SSolomon Peachy return 0; 440a910e4a9SSolomon Peachy } 441a910e4a9SSolomon Peachy 442a910e4a9SSolomon Peachy static void cw1200_free_common(struct ieee80211_hw *dev) 443a910e4a9SSolomon Peachy { 444a910e4a9SSolomon Peachy ieee80211_free_hw(dev); 445a910e4a9SSolomon Peachy } 446a910e4a9SSolomon Peachy 447a910e4a9SSolomon Peachy static void cw1200_unregister_common(struct ieee80211_hw *dev) 448a910e4a9SSolomon Peachy { 449a910e4a9SSolomon Peachy struct cw1200_common *priv = dev->priv; 450a910e4a9SSolomon Peachy int i; 451a910e4a9SSolomon Peachy 452a910e4a9SSolomon Peachy ieee80211_unregister_hw(dev); 453a910e4a9SSolomon Peachy 454a910e4a9SSolomon Peachy del_timer_sync(&priv->mcast_timeout); 455a910e4a9SSolomon Peachy cw1200_unregister_bh(priv); 456a910e4a9SSolomon Peachy 457a910e4a9SSolomon Peachy cw1200_debug_release(priv); 458a910e4a9SSolomon Peachy 459a910e4a9SSolomon Peachy mutex_destroy(&priv->conf_mutex); 460a910e4a9SSolomon Peachy 461a910e4a9SSolomon Peachy wsm_buf_deinit(&priv->wsm_cmd_buf); 462a910e4a9SSolomon Peachy 463a910e4a9SSolomon Peachy destroy_workqueue(priv->workqueue); 464a910e4a9SSolomon Peachy priv->workqueue = NULL; 465a910e4a9SSolomon Peachy 466a910e4a9SSolomon Peachy if (priv->sdd) { 467a910e4a9SSolomon Peachy release_firmware(priv->sdd); 468a910e4a9SSolomon Peachy priv->sdd = NULL; 469a910e4a9SSolomon Peachy } 470a910e4a9SSolomon Peachy 471a910e4a9SSolomon Peachy for (i = 0; i < 4; ++i) 472a910e4a9SSolomon Peachy cw1200_queue_deinit(&priv->tx_queue[i]); 473a910e4a9SSolomon Peachy 474a910e4a9SSolomon Peachy cw1200_queue_stats_deinit(&priv->tx_queue_stats); 4754e17b87eSSolomon Peachy #ifdef CONFIG_PM 476a910e4a9SSolomon Peachy cw1200_pm_deinit(&priv->pm_state); 4774e17b87eSSolomon Peachy #endif 478a910e4a9SSolomon Peachy } 479a910e4a9SSolomon Peachy 480a910e4a9SSolomon Peachy /* Clock is in KHz */ 481a910e4a9SSolomon Peachy u32 cw1200_dpll_from_clk(u16 clk_khz) 482a910e4a9SSolomon Peachy { 483a910e4a9SSolomon Peachy switch (clk_khz) { 484a910e4a9SSolomon Peachy case 0x32C8: /* 13000 KHz */ 485a910e4a9SSolomon Peachy return 0x1D89D241; 486a910e4a9SSolomon Peachy case 0x3E80: /* 16000 KHz */ 487a910e4a9SSolomon Peachy return 0x000001E1; 488a910e4a9SSolomon Peachy case 0x41A0: /* 16800 KHz */ 489a910e4a9SSolomon Peachy return 0x124931C1; 490a910e4a9SSolomon Peachy case 0x4B00: /* 19200 KHz */ 491a910e4a9SSolomon Peachy return 0x00000191; 492a910e4a9SSolomon Peachy case 0x5DC0: /* 24000 KHz */ 493a910e4a9SSolomon Peachy return 0x00000141; 494a910e4a9SSolomon Peachy case 0x6590: /* 26000 KHz */ 495a910e4a9SSolomon Peachy return 0x0EC4F121; 496a910e4a9SSolomon Peachy case 0x8340: /* 33600 KHz */ 497a910e4a9SSolomon Peachy return 0x092490E1; 498a910e4a9SSolomon Peachy case 0x9600: /* 38400 KHz */ 499a910e4a9SSolomon Peachy return 0x100010C1; 500a910e4a9SSolomon Peachy case 0x9C40: /* 40000 KHz */ 501a910e4a9SSolomon Peachy return 0x000000C1; 502a910e4a9SSolomon Peachy case 0xBB80: /* 48000 KHz */ 503a910e4a9SSolomon Peachy return 0x000000A1; 504a910e4a9SSolomon Peachy case 0xCB20: /* 52000 KHz */ 505a910e4a9SSolomon Peachy return 0x07627091; 506a910e4a9SSolomon Peachy default: 5077f190230SSolomon Peachy pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n", 508a910e4a9SSolomon Peachy clk_khz); 509a910e4a9SSolomon Peachy return 0x0EC4F121; 510a910e4a9SSolomon Peachy } 511a910e4a9SSolomon Peachy } 512a910e4a9SSolomon Peachy 513911373ccSSolomon Peachy int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, 514911373ccSSolomon Peachy struct hwbus_priv *hwbus, 515a910e4a9SSolomon Peachy struct device *pdev, 516a910e4a9SSolomon Peachy struct cw1200_common **core, 517a910e4a9SSolomon Peachy int ref_clk, const u8 *macaddr, 518a910e4a9SSolomon Peachy const char *sdd_path, bool have_5ghz) 519a910e4a9SSolomon Peachy { 520a910e4a9SSolomon Peachy int err = -EINVAL; 521a910e4a9SSolomon Peachy struct ieee80211_hw *dev; 522a910e4a9SSolomon Peachy struct cw1200_common *priv; 523a910e4a9SSolomon Peachy struct wsm_operational_mode mode = { 524a910e4a9SSolomon Peachy .power_mode = cw1200_power_mode, 525a910e4a9SSolomon Peachy .disable_more_flag_usage = true, 526a910e4a9SSolomon Peachy }; 527a910e4a9SSolomon Peachy 528a910e4a9SSolomon Peachy dev = cw1200_init_common(macaddr, have_5ghz); 529a910e4a9SSolomon Peachy if (!dev) 530a910e4a9SSolomon Peachy goto err; 531a910e4a9SSolomon Peachy 532a910e4a9SSolomon Peachy priv = dev->priv; 533a910e4a9SSolomon Peachy priv->hw_refclk = ref_clk; 534a910e4a9SSolomon Peachy if (cw1200_refclk) 535a910e4a9SSolomon Peachy priv->hw_refclk = cw1200_refclk; 536a910e4a9SSolomon Peachy 537a910e4a9SSolomon Peachy priv->sdd_path = (char *)sdd_path; 538a910e4a9SSolomon Peachy if (cw1200_sdd_path) 539a910e4a9SSolomon Peachy priv->sdd_path = cw1200_sdd_path; 540a910e4a9SSolomon Peachy 541911373ccSSolomon Peachy priv->hwbus_ops = hwbus_ops; 542911373ccSSolomon Peachy priv->hwbus_priv = hwbus; 543a910e4a9SSolomon Peachy priv->pdev = pdev; 544a910e4a9SSolomon Peachy SET_IEEE80211_DEV(priv->hw, pdev); 545a910e4a9SSolomon Peachy 546a910e4a9SSolomon Peachy /* Pass struct cw1200_common back up */ 547a910e4a9SSolomon Peachy *core = priv; 548a910e4a9SSolomon Peachy 549a910e4a9SSolomon Peachy err = cw1200_register_bh(priv); 550a910e4a9SSolomon Peachy if (err) 551a910e4a9SSolomon Peachy goto err1; 552a910e4a9SSolomon Peachy 553a910e4a9SSolomon Peachy err = cw1200_load_firmware(priv); 554a910e4a9SSolomon Peachy if (err) 555a910e4a9SSolomon Peachy goto err2; 556a910e4a9SSolomon Peachy 557a910e4a9SSolomon Peachy if (wait_event_interruptible_timeout(priv->wsm_startup_done, 558a910e4a9SSolomon Peachy priv->firmware_ready, 559a910e4a9SSolomon Peachy 3*HZ) <= 0) { 560a910e4a9SSolomon Peachy /* TODO: Need to find how to reset device 561a910e4a9SSolomon Peachy in QUEUE mode properly. 562a910e4a9SSolomon Peachy */ 563a910e4a9SSolomon Peachy pr_err("Timeout waiting on device startup\n"); 564a910e4a9SSolomon Peachy err = -ETIMEDOUT; 565a910e4a9SSolomon Peachy goto err2; 566a910e4a9SSolomon Peachy } 567a910e4a9SSolomon Peachy 568a910e4a9SSolomon Peachy /* Set low-power mode. */ 569a910e4a9SSolomon Peachy wsm_set_operational_mode(priv, &mode); 570a910e4a9SSolomon Peachy 571a910e4a9SSolomon Peachy /* Enable multi-TX confirmation */ 572a910e4a9SSolomon Peachy wsm_use_multi_tx_conf(priv, true); 573a910e4a9SSolomon Peachy 574a910e4a9SSolomon Peachy err = cw1200_register_common(dev); 575a910e4a9SSolomon Peachy if (err) 576a910e4a9SSolomon Peachy goto err2; 577a910e4a9SSolomon Peachy 578a910e4a9SSolomon Peachy return err; 579a910e4a9SSolomon Peachy 580a910e4a9SSolomon Peachy err2: 581a910e4a9SSolomon Peachy cw1200_unregister_bh(priv); 582a910e4a9SSolomon Peachy err1: 583a910e4a9SSolomon Peachy cw1200_free_common(dev); 584a910e4a9SSolomon Peachy err: 585a910e4a9SSolomon Peachy *core = NULL; 586a910e4a9SSolomon Peachy return err; 587a910e4a9SSolomon Peachy } 588a910e4a9SSolomon Peachy EXPORT_SYMBOL_GPL(cw1200_core_probe); 589a910e4a9SSolomon Peachy 590a910e4a9SSolomon Peachy void cw1200_core_release(struct cw1200_common *self) 591a910e4a9SSolomon Peachy { 592a910e4a9SSolomon Peachy /* Disable device interrupts */ 593911373ccSSolomon Peachy self->hwbus_ops->lock(self->hwbus_priv); 594a910e4a9SSolomon Peachy __cw1200_irq_enable(self, 0); 595911373ccSSolomon Peachy self->hwbus_ops->unlock(self->hwbus_priv); 596a910e4a9SSolomon Peachy 597a910e4a9SSolomon Peachy /* And then clean up */ 598a910e4a9SSolomon Peachy cw1200_unregister_common(self->hw); 599a910e4a9SSolomon Peachy cw1200_free_common(self->hw); 600a910e4a9SSolomon Peachy return; 601a910e4a9SSolomon Peachy } 602a910e4a9SSolomon Peachy EXPORT_SYMBOL_GPL(cw1200_core_release); 603