xref: /linux/drivers/net/wireless/st/cw1200/main.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
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