iwl-3945.c 65.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/******************************************************************************
 *
 * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 * Contact Information:
 * James P. Ketrenos <ipw2100-admin@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *
 *****************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <linux/firmware.h>
#include <net/mac80211.h>

#include <linux/etherdevice.h>

#include "iwl-3945.h"
43
#include "iwl-helpers.h"
44 45 46 47 48 49 50 51 52 53
#include "iwl-3945-rs.h"

#define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np)    \
	[IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,   \
				    IWL_RATE_##r##M_IEEE,   \
				    IWL_RATE_##ip##M_INDEX, \
				    IWL_RATE_##in##M_INDEX, \
				    IWL_RATE_##rp##M_INDEX, \
				    IWL_RATE_##rn##M_INDEX, \
				    IWL_RATE_##pp##M_INDEX, \
54 55 56
				    IWL_RATE_##np##M_INDEX, \
				    IWL_RATE_##r##M_INDEX_TABLE, \
				    IWL_RATE_##ip##M_INDEX_TABLE }
57 58 59 60 61 62 63 64 65

/*
 * Parameter order:
 *   rate, prev rate, next rate, prev tgg rate, next tgg rate
 *
 * If there isn't a valid next or previous rate then INV is used which
 * maps to IWL_RATE_INVALID
 *
 */
Christoph Hellwig's avatar
Christoph Hellwig committed
66
const struct iwl3945_rate_info iwl3945_rates[IWL_RATE_COUNT] = {
67 68 69 70
	IWL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2),    /*  1mbps */
	IWL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5),          /*  2mbps */
	IWL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11),        /*5.5mbps */
	IWL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18),      /* 11mbps */
71 72 73 74 75 76 77 78 79 80
	IWL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11),        /*  6mbps */
	IWL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11),       /*  9mbps */
	IWL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18),   /* 12mbps */
	IWL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24),   /* 18mbps */
	IWL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36),   /* 24mbps */
	IWL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48),   /* 36mbps */
	IWL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54),   /* 48mbps */
	IWL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV),/* 54mbps */
};

Christoph Hellwig's avatar
Christoph Hellwig committed
81
/* 1 = enable the iwl3945_disable_events() function */
82 83 84 85
#define IWL_EVT_DISABLE (0)
#define IWL_EVT_DISABLE_SIZE (1532/32)

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
86
 * iwl3945_disable_events - Disable selected events in uCode event log
87 88 89 90 91 92 93
 *
 * Disable an event by writing "1"s into "disable"
 *   bitmap in SRAM.  Bit position corresponds to Event # (id/type).
 *   Default values of 0 enable uCode events to be logged.
 * Use for only special debugging.  This function is just a placeholder as-is,
 *   you'll need to provide the special bits! ...
 *   ... and set IWL_EVT_DISABLE to 1. */
Christoph Hellwig's avatar
Christoph Hellwig committed
94
void iwl3945_disable_events(struct iwl3945_priv *priv)
95
{
96
	int ret;
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	int i;
	u32 base;		/* SRAM address of event log header */
	u32 disable_ptr;	/* SRAM address of event-disable bitmap array */
	u32 array_size;		/* # of u32 entries in array */
	u32 evt_disable[IWL_EVT_DISABLE_SIZE] = {
		0x00000000,	/*   31 -    0  Event id numbers */
		0x00000000,	/*   63 -   32 */
		0x00000000,	/*   95 -   64 */
		0x00000000,	/*  127 -   96 */
		0x00000000,	/*  159 -  128 */
		0x00000000,	/*  191 -  160 */
		0x00000000,	/*  223 -  192 */
		0x00000000,	/*  255 -  224 */
		0x00000000,	/*  287 -  256 */
		0x00000000,	/*  319 -  288 */
		0x00000000,	/*  351 -  320 */
		0x00000000,	/*  383 -  352 */
		0x00000000,	/*  415 -  384 */
		0x00000000,	/*  447 -  416 */
		0x00000000,	/*  479 -  448 */
		0x00000000,	/*  511 -  480 */
		0x00000000,	/*  543 -  512 */
		0x00000000,	/*  575 -  544 */
		0x00000000,	/*  607 -  576 */
		0x00000000,	/*  639 -  608 */
		0x00000000,	/*  671 -  640 */
		0x00000000,	/*  703 -  672 */
		0x00000000,	/*  735 -  704 */
		0x00000000,	/*  767 -  736 */
		0x00000000,	/*  799 -  768 */
		0x00000000,	/*  831 -  800 */
		0x00000000,	/*  863 -  832 */
		0x00000000,	/*  895 -  864 */
		0x00000000,	/*  927 -  896 */
		0x00000000,	/*  959 -  928 */
		0x00000000,	/*  991 -  960 */
		0x00000000,	/* 1023 -  992 */
		0x00000000,	/* 1055 - 1024 */
		0x00000000,	/* 1087 - 1056 */
		0x00000000,	/* 1119 - 1088 */
		0x00000000,	/* 1151 - 1120 */
		0x00000000,	/* 1183 - 1152 */
		0x00000000,	/* 1215 - 1184 */
		0x00000000,	/* 1247 - 1216 */
		0x00000000,	/* 1279 - 1248 */
		0x00000000,	/* 1311 - 1280 */
		0x00000000,	/* 1343 - 1312 */
		0x00000000,	/* 1375 - 1344 */
		0x00000000,	/* 1407 - 1376 */
		0x00000000,	/* 1439 - 1408 */
		0x00000000,	/* 1471 - 1440 */
		0x00000000,	/* 1503 - 1472 */
	};

	base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
Christoph Hellwig's avatar
Christoph Hellwig committed
152
	if (!iwl3945_hw_valid_rtc_data_addr(base)) {
153 154 155 156
		IWL_ERROR("Invalid event log pointer 0x%08X\n", base);
		return;
	}

Christoph Hellwig's avatar
Christoph Hellwig committed
157
	ret = iwl3945_grab_nic_access(priv);
158
	if (ret) {
159 160 161 162
		IWL_WARNING("Can not read from adapter at this time.\n");
		return;
	}

Christoph Hellwig's avatar
Christoph Hellwig committed
163 164 165
	disable_ptr = iwl3945_read_targ_mem(priv, base + (4 * sizeof(u32)));
	array_size = iwl3945_read_targ_mem(priv, base + (5 * sizeof(u32)));
	iwl3945_release_nic_access(priv);
166 167 168 169

	if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) {
		IWL_DEBUG_INFO("Disabling selected uCode log events at 0x%x\n",
			       disable_ptr);
Christoph Hellwig's avatar
Christoph Hellwig committed
170
		ret = iwl3945_grab_nic_access(priv);
171
		for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++)
Christoph Hellwig's avatar
Christoph Hellwig committed
172
			iwl3945_write_targ_mem(priv,
173 174
					   disable_ptr + (i * sizeof(u32)),
					   evt_disable[i]);
175

Christoph Hellwig's avatar
Christoph Hellwig committed
176
		iwl3945_release_nic_access(priv);
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	} else {
		IWL_DEBUG_INFO("Selected uCode log events may be disabled\n");
		IWL_DEBUG_INFO("  by writing \"1\"s into disable bitmap\n");
		IWL_DEBUG_INFO("  in SRAM at 0x%x, size %d u32s\n",
			       disable_ptr, array_size);
	}

}

/**
 * iwl3945_get_antenna_flags - Get antenna flags for RXON command
 * @priv: eeprom and antenna fields are used to determine antenna flags
 *
 * priv->eeprom  is used to determine if antenna AUX/MAIN are reversed
 * priv->antenna specifies the antenna diversity mode:
 *
 * IWL_ANTENNA_DIVERISTY - NIC selects best antenna by itself
 * IWL_ANTENNA_MAIN      - Force MAIN antenna
 * IWL_ANTENNA_AUX       - Force AUX antenna
 */
Christoph Hellwig's avatar
Christoph Hellwig committed
197
__le32 iwl3945_get_antenna_flags(const struct iwl3945_priv *priv)
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
{
	switch (priv->antenna) {
	case IWL_ANTENNA_DIVERSITY:
		return 0;

	case IWL_ANTENNA_MAIN:
		if (priv->eeprom.antenna_switch_type)
			return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK;
		return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK;

	case IWL_ANTENNA_AUX:
		if (priv->eeprom.antenna_switch_type)
			return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK;
		return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK;
	}

	/* bad antenna selector value */
	IWL_ERROR("Bad antenna selector value (0x%x)\n", priv->antenna);
	return 0;		/* "diversity" is default if error */
}

/*****************************************************************************
 *
 * Intel PRO/Wireless 3945ABG/BG Network Connection
 *
 *  RX handler implementations
 *
 *  Used by iwl-base.c
 *
 *****************************************************************************/

Christoph Hellwig's avatar
Christoph Hellwig committed
229
void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb)
230
{
Christoph Hellwig's avatar
Christoph Hellwig committed
231
	struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
232
	IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n",
Christoph Hellwig's avatar
Christoph Hellwig committed
233
		     (int)sizeof(struct iwl3945_notif_statistics),
234 235 236 237 238 239 240
		     le32_to_cpu(pkt->len));

	memcpy(&priv->statistics, pkt->u.raw, sizeof(priv->statistics));

	priv->last_statistics_time = jiffies;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
241 242
static void iwl3945_handle_data_packet(struct iwl3945_priv *priv, int is_data,
				   struct iwl3945_rx_mem_buffer *rxb,
243 244 245 246
				   struct ieee80211_rx_status *stats,
				   u16 phy_flags)
{
	struct ieee80211_hdr *hdr;
Christoph Hellwig's avatar
Christoph Hellwig committed
247 248 249
	struct iwl3945_rx_packet *pkt = (struct iwl3945_rx_packet *)rxb->skb->data;
	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
	short len = le16_to_cpu(rx_hdr->len);

	/* We received data from the HW, so stop the watchdog */
	if (unlikely((len + IWL_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
		IWL_DEBUG_DROP("Corruption detected!\n");
		return;
	}

	/* We only process data packets if the interface is open */
	if (unlikely(!priv->is_open)) {
		IWL_DEBUG_DROP_LIMIT
		    ("Dropping packet while interface is not open.\n");
		return;
	}
	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
Christoph Hellwig's avatar
Christoph Hellwig committed
265 266
		if (iwl3945_param_hwcrypto)
			iwl3945_set_decrypted_flag(priv, rxb->skb,
267 268
					       le32_to_cpu(rx_end->status),
					       stats);
Christoph Hellwig's avatar
Christoph Hellwig committed
269
		iwl3945_handle_data_packet_monitor(priv, rxb, IWL_RX_DATA(pkt),
270 271 272 273 274 275 276 277 278 279
					       len, stats, phy_flags);
		return;
	}

	skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt);
	/* Set the size of the skb to the size of the frame */
	skb_put(rxb->skb, le16_to_cpu(rx_hdr->len));

	hdr = (void *)rxb->skb->data;

Christoph Hellwig's avatar
Christoph Hellwig committed
280 281
	if (iwl3945_param_hwcrypto)
		iwl3945_set_decrypted_flag(priv, rxb->skb,
282 283 284 285 286 287
				       le32_to_cpu(rx_end->status), stats);

	ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
	rxb->skb = NULL;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
288 289
static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
				struct iwl3945_rx_mem_buffer *rxb)
290
{
Christoph Hellwig's avatar
Christoph Hellwig committed
291 292 293 294
	struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
	struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
	struct ieee80211_hdr *header;
	u16 phy_flags = le16_to_cpu(rx_hdr->phy_flags);
	u16 rx_stats_sig_avg = le16_to_cpu(rx_stats->sig_avg);
	u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff);
	struct ieee80211_rx_status stats = {
		.mactime = le64_to_cpu(rx_end->timestamp),
		.freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel)),
		.channel = le16_to_cpu(rx_hdr->channel),
		.phymode = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
		MODE_IEEE80211G : MODE_IEEE80211A,
		.antenna = 0,
		.rate = rx_hdr->rate,
		.flag = 0,
	};
	u8 network_packet;
	int snr;

	if ((unlikely(rx_stats->phy_count > 20))) {
		IWL_DEBUG_DROP
		    ("dsp size out of range [0,20]: "
		     "%d/n", rx_stats->phy_count);
		return;
	}

	if (!(rx_end->status & RX_RES_STATUS_NO_CRC32_ERROR)
	    || !(rx_end->status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
		IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status);
		return;
	}

	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
		iwl3945_handle_data_packet(priv, 1, rxb, &stats, phy_flags);
		return;
	}

	/* Convert 3945's rssi indicator to dBm */
	stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;

	/* Set default noise value to -127 */
	if (priv->last_rx_noise == 0)
		priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;

	/* 3945 provides noise info for OFDM frames only.
	 * sig_avg and noise_diff are measured by the 3945's digital signal
	 *   processor (DSP), and indicate linear levels of signal level and
	 *   distortion/noise within the packet preamble after
	 *   automatic gain control (AGC).  sig_avg should stay fairly
	 *   constant if the radio's AGC is working well.
	 * Since these values are linear (not dB or dBm), linear
	 *   signal-to-noise ratio (SNR) is (sig_avg / noise_diff).
	 * Convert linear SNR to dB SNR, then subtract that from rssi dBm
	 *   to obtain noise level in dBm.
	 * Calculate stats.signal (quality indicator in %) based on SNR. */
	if (rx_stats_noise_diff) {
		snr = rx_stats_sig_avg / rx_stats_noise_diff;
Christoph Hellwig's avatar
Christoph Hellwig committed
350 351
		stats.noise = stats.ssi - iwl3945_calc_db_from_ratio(snr);
		stats.signal = iwl3945_calc_sig_qual(stats.ssi, stats.noise);
352 353 354 355 356

	/* If noise info not available, calculate signal quality indicator (%)
	 *   using just the dBm signal level. */
	} else {
		stats.noise = priv->last_rx_noise;
Christoph Hellwig's avatar
Christoph Hellwig committed
357
		stats.signal = iwl3945_calc_sig_qual(stats.ssi, 0);
358 359 360 361 362 363 364 365 366
	}


	IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n",
			stats.ssi, stats.noise, stats.signal,
			rx_stats_sig_avg, rx_stats_noise_diff);

	stats.freq = ieee80211chan2mhz(stats.channel);

Christoph Hellwig's avatar
Christoph Hellwig committed
367
	/* can be covered by iwl3945_report_frame() in most cases */
368 369 370 371
/*      IWL_DEBUG_RX("RX status: 0x%08X\n", rx_end->status); */

	header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);

Christoph Hellwig's avatar
Christoph Hellwig committed
372
	network_packet = iwl3945_is_network_packet(priv, header);
373

374
#ifdef CONFIG_IWL3945_DEBUG
Christoph Hellwig's avatar
Christoph Hellwig committed
375
	if (iwl3945_debug_level & IWL_DL_STATS && net_ratelimit())
376 377 378 379 380 381
		IWL_DEBUG_STATS
		    ("[%c] %d RSSI: %d Signal: %u, Noise: %u, Rate: %u\n",
		     network_packet ? '*' : ' ',
		     stats.channel, stats.ssi, stats.ssi,
		     stats.ssi, stats.rate);

Christoph Hellwig's avatar
Christoph Hellwig committed
382
	if (iwl3945_debug_level & (IWL_DL_RX))
383
		/* Set "1" to report good data frames in groups of 100 */
Christoph Hellwig's avatar
Christoph Hellwig committed
384
		iwl3945_report_frame(priv, pkt, header, 1);
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
#endif

	if (network_packet) {
		priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp);
		priv->last_tsf = le64_to_cpu(rx_end->timestamp);
		priv->last_rx_rssi = stats.ssi;
		priv->last_rx_noise = stats.noise;
	}

	switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
	case IEEE80211_FTYPE_MGMT:
		switch (le16_to_cpu(header->frame_control) &
			IEEE80211_FCTL_STYPE) {
		case IEEE80211_STYPE_PROBE_RESP:
		case IEEE80211_STYPE_BEACON:{
				/* If this is a beacon or probe response for
				 * our network then cache the beacon
				 * timestamp */
				if ((((priv->iw_mode == IEEE80211_IF_TYPE_STA)
				      && !compare_ether_addr(header->addr2,
							     priv->bssid)) ||
				     ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
				      && !compare_ether_addr(header->addr3,
							     priv->bssid)))) {
					struct ieee80211_mgmt *mgmt =
					    (struct ieee80211_mgmt *)header;
					__le32 *pos;
					pos =
					    (__le32 *) & mgmt->u.beacon.
					    timestamp;
					priv->timestamp0 = le32_to_cpu(pos[0]);
					priv->timestamp1 = le32_to_cpu(pos[1]);
					priv->beacon_int = le16_to_cpu(
					    mgmt->u.beacon.beacon_int);
					if (priv->call_post_assoc_from_beacon &&
					    (priv->iw_mode ==
						IEEE80211_IF_TYPE_STA))
						queue_work(priv->workqueue,
						    &priv->post_associate.work);

					priv->call_post_assoc_from_beacon = 0;
				}

				break;
			}

		case IEEE80211_STYPE_ACTION:
			/* TODO: Parse 802.11h frames for CSA... */
			break;

			/*
			 * TODO: There is no callback function from upper
			 * stack to inform us when associated status. this
			 * work around to sniff assoc_resp management frame
			 * and finish the association process.
			 */
		case IEEE80211_STYPE_ASSOC_RESP:
		case IEEE80211_STYPE_REASSOC_RESP:{
				struct ieee80211_mgmt *mgnt =
				    (struct ieee80211_mgmt *)header;
				priv->assoc_id = (~((1 << 15) | (1 << 14)) &
						  le16_to_cpu(mgnt->u.
							      assoc_resp.aid));
				priv->assoc_capability =
				    le16_to_cpu(mgnt->u.assoc_resp.capab_info);
				if (priv->beacon_int)
					queue_work(priv->workqueue,
					    &priv->post_associate.work);
				else
					priv->call_post_assoc_from_beacon = 1;
				break;
			}

		case IEEE80211_STYPE_PROBE_REQ:{
459 460 461
				DECLARE_MAC_BUF(mac1);
				DECLARE_MAC_BUF(mac2);
				DECLARE_MAC_BUF(mac3);
462 463
				if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
					IWL_DEBUG_DROP
464 465 466 467 468
					    ("Dropping (non network): %s"
					     ", %s, %s\n",
					     print_mac(mac1, header->addr1),
					     print_mac(mac2, header->addr2),
					     print_mac(mac3, header->addr3));
469 470 471 472 473 474 475 476 477 478
				return;
			}
		}

		iwl3945_handle_data_packet(priv, 0, rxb, &stats, phy_flags);
		break;

	case IEEE80211_FTYPE_CTL:
		break;

479 480 481 482 483
	case IEEE80211_FTYPE_DATA: {
		DECLARE_MAC_BUF(mac1);
		DECLARE_MAC_BUF(mac2);
		DECLARE_MAC_BUF(mac3);

Christoph Hellwig's avatar
Christoph Hellwig committed
484
		if (unlikely(iwl3945_is_duplicate_packet(priv, header)))
485 486 487 488
			IWL_DEBUG_DROP("Dropping (dup): %s, %s, %s\n",
				       print_mac(mac1, header->addr1),
				       print_mac(mac2, header->addr2),
				       print_mac(mac3, header->addr3));
489 490 491 492 493
		else
			iwl3945_handle_data_packet(priv, 1, rxb, &stats,
						   phy_flags);
		break;
	}
494
	}
495 496
}

Christoph Hellwig's avatar
Christoph Hellwig committed
497
int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *ptr,
498 499 500 501
				 dma_addr_t addr, u16 len)
{
	int count;
	u32 pad;
Christoph Hellwig's avatar
Christoph Hellwig committed
502
	struct iwl3945_tfd_frame *tfd = (struct iwl3945_tfd_frame *)ptr;
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524

	count = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags));
	pad = TFD_CTL_PAD_GET(le32_to_cpu(tfd->control_flags));

	if ((count >= NUM_TFD_CHUNKS) || (count < 0)) {
		IWL_ERROR("Error can not send more than %d chunks\n",
			  NUM_TFD_CHUNKS);
		return -EINVAL;
	}

	tfd->pa[count].addr = cpu_to_le32(addr);
	tfd->pa[count].len = cpu_to_le32(len);

	count++;

	tfd->control_flags = cpu_to_le32(TFD_CTL_COUNT_SET(count) |
					 TFD_CTL_PAD_SET(pad));

	return 0;
}

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
525
 * iwl3945_hw_txq_free_tfd - Free one TFD, those at index [txq->q.read_ptr]
526 527 528
 *
 * Does NOT advance any indexes
 */
Christoph Hellwig's avatar
Christoph Hellwig committed
529
int iwl3945_hw_txq_free_tfd(struct iwl3945_priv *priv, struct iwl3945_tx_queue *txq)
530
{
Christoph Hellwig's avatar
Christoph Hellwig committed
531 532
	struct iwl3945_tfd_frame *bd_tmp = (struct iwl3945_tfd_frame *)&txq->bd[0];
	struct iwl3945_tfd_frame *bd = &bd_tmp[txq->q.read_ptr];
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
	struct pci_dev *dev = priv->pci_dev;
	int i;
	int counter;

	/* classify bd */
	if (txq->q.id == IWL_CMD_QUEUE_NUM)
		/* nothing to cleanup after for host commands */
		return 0;

	/* sanity check */
	counter = TFD_CTL_COUNT_GET(le32_to_cpu(bd->control_flags));
	if (counter > NUM_TFD_CHUNKS) {
		IWL_ERROR("Too many chunks: %i\n", counter);
		/* @todo issue fatal error, it is quite serious situation */
		return 0;
	}

	/* unmap chunks if any */

	for (i = 1; i < counter; i++) {
		pci_unmap_single(dev, le32_to_cpu(bd->pa[i].addr),
				 le32_to_cpu(bd->pa[i].len), PCI_DMA_TODEVICE);
555 556 557
		if (txq->txb[txq->q.read_ptr].skb[0]) {
			struct sk_buff *skb = txq->txb[txq->q.read_ptr].skb[0];
			if (txq->txb[txq->q.read_ptr].skb[0]) {
558 559
				/* Can be called from interrupt context */
				dev_kfree_skb_any(skb);
560
				txq->txb[txq->q.read_ptr].skb[0] = NULL;
561 562 563 564 565 566
			}
		}
	}
	return 0;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
567
u8 iwl3945_hw_find_station(struct iwl3945_priv *priv, const u8 *addr)
568 569 570 571
{
	int i;
	int ret = IWL_INVALID_STATION;
	unsigned long flags;
572
	DECLARE_MAC_BUF(mac);
573 574 575 576 577 578 579 580 581 582

	spin_lock_irqsave(&priv->sta_lock, flags);
	for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++)
		if ((priv->stations[i].used) &&
		    (!compare_ether_addr
		     (priv->stations[i].sta.sta.addr, addr))) {
			ret = i;
			goto out;
		}

583 584
	IWL_DEBUG_INFO("can not find STA %s (total %d)\n",
		       print_mac(mac, addr), priv->num_stations);
585 586 587 588 589 590
 out:
	spin_unlock_irqrestore(&priv->sta_lock, flags);
	return ret;
}

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
591
 * iwl3945_hw_build_tx_cmd_rate - Add rate portion to TX_CMD:
592 593
 *
*/
Christoph Hellwig's avatar
Christoph Hellwig committed
594 595
void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv,
			      struct iwl3945_cmd *cmd,
596 597 598 599 600 601 602 603 604 605 606 607
			      struct ieee80211_tx_control *ctrl,
			      struct ieee80211_hdr *hdr, int sta_id, int tx_id)
{
	unsigned long flags;
	u16 rate_index = min(ctrl->tx_rate & 0xffff, IWL_RATE_COUNT - 1);
	u16 rate_mask;
	int rate;
	u8 rts_retry_limit;
	u8 data_retry_limit;
	__le32 tx_flags;
	u16 fc = le16_to_cpu(hdr->frame_control);

Christoph Hellwig's avatar
Christoph Hellwig committed
608
	rate = iwl3945_rates[rate_index].plcp;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
	tx_flags = cmd->cmd.tx.tx_flags;

	/* We need to figure out how to get the sta->supp_rates while
	 * in this running context; perhaps encoding into ctrl->tx_rate? */
	rate_mask = IWL_RATES_MASK;

	spin_lock_irqsave(&priv->sta_lock, flags);

	priv->stations[sta_id].current_rate.rate_n_flags = rate;

	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
	    (sta_id != IWL3945_BROADCAST_ID) &&
		(sta_id != IWL_MULTICAST_ID))
		priv->stations[IWL_STA_ID].current_rate.rate_n_flags = rate;

	spin_unlock_irqrestore(&priv->sta_lock, flags);

	if (tx_id >= IWL_CMD_QUEUE_NUM)
		rts_retry_limit = 3;
	else
		rts_retry_limit = 7;

	if (ieee80211_is_probe_response(fc)) {
		data_retry_limit = 3;
		if (data_retry_limit < rts_retry_limit)
			rts_retry_limit = data_retry_limit;
	} else
		data_retry_limit = IWL_DEFAULT_TX_RETRY;

	if (priv->data_retry_limit != -1)
		data_retry_limit = priv->data_retry_limit;

	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
		switch (fc & IEEE80211_FCTL_STYPE) {
		case IEEE80211_STYPE_AUTH:
		case IEEE80211_STYPE_DEAUTH:
		case IEEE80211_STYPE_ASSOC_REQ:
		case IEEE80211_STYPE_REASSOC_REQ:
			if (tx_flags & TX_CMD_FLG_RTS_MSK) {
				tx_flags &= ~TX_CMD_FLG_RTS_MSK;
				tx_flags |= TX_CMD_FLG_CTS_MSK;
			}
			break;
		default:
			break;
		}
	}

	cmd->cmd.tx.rts_retry_limit = rts_retry_limit;
	cmd->cmd.tx.data_retry_limit = data_retry_limit;
	cmd->cmd.tx.rate = rate;
	cmd->cmd.tx.tx_flags = tx_flags;

	/* OFDM */
663 664
	cmd->cmd.tx.supp_rates[0] =
	   ((rate_mask & IWL_OFDM_RATES_MASK) >> IWL_FIRST_OFDM_RATE) & 0xFF;
665 666

	/* CCK */
667
	cmd->cmd.tx.supp_rates[1] = (rate_mask & 0xF);
668 669 670 671 672 673 674

	IWL_DEBUG_RATE("Tx sta id: %d, rate: %d (plcp), flags: 0x%4X "
		       "cck/ofdm mask: 0x%x/0x%x\n", sta_id,
		       cmd->cmd.tx.rate, le32_to_cpu(cmd->cmd.tx.tx_flags),
		       cmd->cmd.tx.supp_rates[1], cmd->cmd.tx.supp_rates[0]);
}

Christoph Hellwig's avatar
Christoph Hellwig committed
675
u8 iwl3945_sync_sta(struct iwl3945_priv *priv, int sta_id, u16 tx_rate, u8 flags)
676 677
{
	unsigned long flags_spin;
Christoph Hellwig's avatar
Christoph Hellwig committed
678
	struct iwl3945_station_entry *station;
679 680 681 682 683 684 685 686 687 688 689 690 691 692

	if (sta_id == IWL_INVALID_STATION)
		return IWL_INVALID_STATION;

	spin_lock_irqsave(&priv->sta_lock, flags_spin);
	station = &priv->stations[sta_id];

	station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK;
	station->sta.rate_n_flags = cpu_to_le16(tx_rate);
	station->current_rate.rate_n_flags = tx_rate;
	station->sta.mode = STA_CONTROL_MODIFY_MSK;

	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);

Christoph Hellwig's avatar
Christoph Hellwig committed
693
	iwl3945_send_add_station(priv, &station->sta, flags);
694 695 696 697 698
	IWL_DEBUG_RATE("SCALE sync station %d to rate %d\n",
			sta_id, tx_rate);
	return sta_id;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
699
static int iwl3945_nic_set_pwr_src(struct iwl3945_priv *priv, int pwr_max)
700 701 702 703 704
{
	int rc;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
Christoph Hellwig's avatar
Christoph Hellwig committed
705
	rc = iwl3945_grab_nic_access(priv);
706 707 708 709 710 711 712 713 714 715 716
	if (rc) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return rc;
	}

	if (!pwr_max) {
		u32 val;

		rc = pci_read_config_dword(priv->pci_dev,
				PCI_POWER_SOURCE, &val);
		if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) {
Christoph Hellwig's avatar
Christoph Hellwig committed
717
			iwl3945_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
718 719
					APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
					~APMG_PS_CTRL_MSK_PWR_SRC);
Christoph Hellwig's avatar
Christoph Hellwig committed
720
			iwl3945_release_nic_access(priv);
721

Christoph Hellwig's avatar
Christoph Hellwig committed
722
			iwl3945_poll_bit(priv, CSR_GPIO_IN,
723 724 725
				     CSR_GPIO_IN_VAL_VAUX_PWR_SRC,
				     CSR_GPIO_IN_BIT_AUX_POWER, 5000);
		} else
Christoph Hellwig's avatar
Christoph Hellwig committed
726
			iwl3945_release_nic_access(priv);
727
	} else {
Christoph Hellwig's avatar
Christoph Hellwig committed
728
		iwl3945_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
729 730 731
				APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
				~APMG_PS_CTRL_MSK_PWR_SRC);

Christoph Hellwig's avatar
Christoph Hellwig committed
732 733
		iwl3945_release_nic_access(priv);
		iwl3945_poll_bit(priv, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC,
734 735 736 737 738 739 740
			     CSR_GPIO_IN_BIT_AUX_POWER, 5000);	/* uS */
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	return rc;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
741
static int iwl3945_rx_init(struct iwl3945_priv *priv, struct iwl3945_rx_queue *rxq)
742 743 744 745 746
{
	int rc;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
Christoph Hellwig's avatar
Christoph Hellwig committed
747
	rc = iwl3945_grab_nic_access(priv);
748 749 750 751 752
	if (rc) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return rc;
	}

Christoph Hellwig's avatar
Christoph Hellwig committed
753 754
	iwl3945_write_direct32(priv, FH_RCSR_RBD_BASE(0), rxq->dma_addr);
	iwl3945_write_direct32(priv, FH_RCSR_RPTR_ADDR(0),
755
			     priv->hw_setting.shared_phys +
Christoph Hellwig's avatar
Christoph Hellwig committed
756 757 758
			     offsetof(struct iwl3945_shared, rx_read_ptr[0]));
	iwl3945_write_direct32(priv, FH_RCSR_WPTR(0), 0);
	iwl3945_write_direct32(priv, FH_RCSR_CONFIG(0),
759 760 761 762 763 764 765 766 767 768
		ALM_FH_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE |
		ALM_FH_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE |
		ALM_FH_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN |
		ALM_FH_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 |
		(RX_QUEUE_SIZE_LOG << ALM_FH_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE) |
		ALM_FH_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST |
		(1 << ALM_FH_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH) |
		ALM_FH_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH);

	/* fake read to flush all prev I/O */
Christoph Hellwig's avatar
Christoph Hellwig committed
769
	iwl3945_read_direct32(priv, FH_RSSR_CTRL);
770

Christoph Hellwig's avatar
Christoph Hellwig committed
771
	iwl3945_release_nic_access(priv);
772 773 774 775 776
	spin_unlock_irqrestore(&priv->lock, flags);

	return 0;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
777
static int iwl3945_tx_reset(struct iwl3945_priv *priv)
778 779 780 781 782
{
	int rc;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
Christoph Hellwig's avatar
Christoph Hellwig committed
783
	rc = iwl3945_grab_nic_access(priv);
784 785 786 787 788 789
	if (rc) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return rc;
	}

	/* bypass mode */
Christoph Hellwig's avatar
Christoph Hellwig committed
790
	iwl3945_write_prph(priv, ALM_SCD_MODE_REG, 0x2);
791 792

	/* RA 0 is active */
Christoph Hellwig's avatar
Christoph Hellwig committed
793
	iwl3945_write_prph(priv, ALM_SCD_ARASTAT_REG, 0x01);
794 795

	/* all 6 fifo are active */
Christoph Hellwig's avatar
Christoph Hellwig committed
796
	iwl3945_write_prph(priv, ALM_SCD_TXFACT_REG, 0x3f);
797

Christoph Hellwig's avatar
Christoph Hellwig committed
798 799 800 801
	iwl3945_write_prph(priv, ALM_SCD_SBYP_MODE_1_REG, 0x010000);
	iwl3945_write_prph(priv, ALM_SCD_SBYP_MODE_2_REG, 0x030002);
	iwl3945_write_prph(priv, ALM_SCD_TXF4MF_REG, 0x000004);
	iwl3945_write_prph(priv, ALM_SCD_TXF5MF_REG, 0x000005);
802

Christoph Hellwig's avatar
Christoph Hellwig committed
803
	iwl3945_write_direct32(priv, FH_TSSR_CBB_BASE,
804 805
			     priv->hw_setting.shared_phys);

Christoph Hellwig's avatar
Christoph Hellwig committed
806
	iwl3945_write_direct32(priv, FH_TSSR_MSG_CONFIG,
807 808 809 810 811 812 813 814
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON |
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON |
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B |
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON |
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON |
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH |
		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH);

Christoph Hellwig's avatar
Christoph Hellwig committed
815
	iwl3945_release_nic_access(priv);
816 817 818 819 820 821 822 823 824 825
	spin_unlock_irqrestore(&priv->lock, flags);

	return 0;
}

/**
 * iwl3945_txq_ctx_reset - Reset TX queue context
 *
 * Destroys all DMA structures and initialize them again
 */
Christoph Hellwig's avatar
Christoph Hellwig committed
826
static int iwl3945_txq_ctx_reset(struct iwl3945_priv *priv)
827 828 829 830
{
	int rc;
	int txq_id, slots_num;

Christoph Hellwig's avatar
Christoph Hellwig committed
831
	iwl3945_hw_txq_ctx_free(priv);
832 833 834 835 836 837 838 839 840 841

	/* Tx CMD queue */
	rc = iwl3945_tx_reset(priv);
	if (rc)
		goto error;

	/* Tx queue(s) */
	for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
		slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
				TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
Christoph Hellwig's avatar
Christoph Hellwig committed
842
		rc = iwl3945_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
843 844 845 846 847 848 849 850 851 852
				txq_id);
		if (rc) {
			IWL_ERROR("Tx %d queue init failed\n", txq_id);
			goto error;
		}
	}

	return rc;

 error:
Christoph Hellwig's avatar
Christoph Hellwig committed
853
	iwl3945_hw_txq_ctx_free(priv);
854 855 856
	return rc;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
857
int iwl3945_hw_nic_init(struct iwl3945_priv *priv)
858 859 860 861
{
	u8 rev_id;
	int rc;
	unsigned long flags;
Christoph Hellwig's avatar
Christoph Hellwig committed
862
	struct iwl3945_rx_queue *rxq = &priv->rxq;
863

Christoph Hellwig's avatar
Christoph Hellwig committed
864
	iwl3945_power_init_handle(priv);
865 866

	spin_lock_irqsave(&priv->lock, flags);
Christoph Hellwig's avatar
Christoph Hellwig committed
867 868
	iwl3945_set_bit(priv, CSR_ANA_PLL_CFG, (1 << 24));
	iwl3945_set_bit(priv, CSR_GIO_CHICKEN_BITS,
869 870
		    CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);

Christoph Hellwig's avatar
Christoph Hellwig committed
871 872
	iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
	rc = iwl3945_poll_bit(priv, CSR_GP_CNTRL,
873 874 875 876 877 878 879 880
			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
	if (rc < 0) {
		spin_unlock_irqrestore(&priv->lock, flags);
		IWL_DEBUG_INFO("Failed to init the card\n");
		return rc;
	}

Christoph Hellwig's avatar
Christoph Hellwig committed
881
	rc = iwl3945_grab_nic_access(priv);
882 883 884 885
	if (rc) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return rc;
	}
Christoph Hellwig's avatar
Christoph Hellwig committed
886
	iwl3945_write_prph(priv, APMG_CLK_EN_REG,
887 888 889
				 APMG_CLK_VAL_DMA_CLK_RQT |
				 APMG_CLK_VAL_BSM_CLK_RQT);
	udelay(20);
Christoph Hellwig's avatar
Christoph Hellwig committed
890
	iwl3945_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
891
				    APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
Christoph Hellwig's avatar
Christoph Hellwig committed
892
	iwl3945_release_nic_access(priv);
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
	spin_unlock_irqrestore(&priv->lock, flags);

	/* Determine HW type */
	rc = pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id);
	if (rc)
		return rc;
	IWL_DEBUG_INFO("HW Revision ID = 0x%X\n", rev_id);

	iwl3945_nic_set_pwr_src(priv, 1);
	spin_lock_irqsave(&priv->lock, flags);

	if (rev_id & PCI_CFG_REV_ID_BIT_RTP)
		IWL_DEBUG_INFO("RTP type \n");
	else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) {
		IWL_DEBUG_INFO("ALM-MB type\n");
Christoph Hellwig's avatar
Christoph Hellwig committed
908
		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
909 910 911
			    CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB);
	} else {
		IWL_DEBUG_INFO("ALM-MM type\n");
Christoph Hellwig's avatar
Christoph Hellwig committed
912
		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
913 914 915 916 917 918
			    CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM);
	}

	spin_unlock_irqrestore(&priv->lock, flags);

	/* Initialize the EEPROM */
Christoph Hellwig's avatar
Christoph Hellwig committed
919
	rc = iwl3945_eeprom_init(priv);
920 921 922 923 924 925
	if (rc)
		return rc;

	spin_lock_irqsave(&priv->lock, flags);
	if (EEPROM_SKU_CAP_OP_MODE_MRC == priv->eeprom.sku_cap) {
		IWL_DEBUG_INFO("SKU OP mode is mrc\n");
Christoph Hellwig's avatar
Christoph Hellwig committed
926
		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
927 928 929 930 931 932 933
			    CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC);
	} else
		IWL_DEBUG_INFO("SKU OP mode is basic\n");

	if ((priv->eeprom.board_revision & 0xF0) == 0xD0) {
		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
			       priv->eeprom.board_revision);
Christoph Hellwig's avatar
Christoph Hellwig committed
934
		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
935 936 937 938
			    CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
	} else {
		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
			       priv->eeprom.board_revision);
Christoph Hellwig's avatar
Christoph Hellwig committed
939
		iwl3945_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
940 941 942 943
			      CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
	}

	if (priv->eeprom.almgor_m_version <= 1) {
Christoph Hellwig's avatar
Christoph Hellwig committed
944
		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
945 946 947 948 949 950
			    CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
		IWL_DEBUG_INFO("Card M type A version is 0x%X\n",
			       priv->eeprom.almgor_m_version);
	} else {
		IWL_DEBUG_INFO("Card M type B version is 0x%X\n",
			       priv->eeprom.almgor_m_version);
Christoph Hellwig's avatar
Christoph Hellwig committed
951
		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
952 953 954 955 956 957 958 959 960 961 962 963
			    CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE)
		IWL_DEBUG_RF_KILL("SW RF KILL supported in EEPROM.\n");

	if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE)
		IWL_DEBUG_RF_KILL("HW RF KILL supported in EEPROM.\n");

	/* Allocate the RX queue, or reset if it is already allocated */
	if (!rxq->bd) {
Christoph Hellwig's avatar
Christoph Hellwig committed
964
		rc = iwl3945_rx_queue_alloc(priv);
965 966 967 968 969
		if (rc) {
			IWL_ERROR("Unable to initialize Rx queue\n");
			return -ENOMEM;
		}
	} else
Christoph Hellwig's avatar
Christoph Hellwig committed
970
		iwl3945_rx_queue_reset(priv, rxq);
971

Christoph Hellwig's avatar
Christoph Hellwig committed
972
	iwl3945_rx_replenish(priv);
973 974 975 976 977 978 979

	iwl3945_rx_init(priv, rxq);

	spin_lock_irqsave(&priv->lock, flags);

	/* Look at using this instead:
	rxq->need_update = 1;
Christoph Hellwig's avatar
Christoph Hellwig committed
980
	iwl3945_rx_queue_update_write_ptr(priv, rxq);
981 982
	*/

Christoph Hellwig's avatar
Christoph Hellwig committed
983
	rc = iwl3945_grab_nic_access(priv);
984 985 986 987
	if (rc) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return rc;
	}
Christoph Hellwig's avatar
Christoph Hellwig committed
988 989
	iwl3945_write_direct32(priv, FH_RCSR_WPTR(0), rxq->write & ~7);
	iwl3945_release_nic_access(priv);
990 991 992 993 994 995 996 997 998 999 1000 1001 1002

	spin_unlock_irqrestore(&priv->lock, flags);

	rc = iwl3945_txq_ctx_reset(priv);
	if (rc)
		return rc;

	set_bit(STATUS_INIT, &priv->status);

	return 0;
}

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
1003
 * iwl3945_hw_txq_ctx_free - Free TXQ Context
1004 1005 1006
 *
 * Destroy all TX DMA queues and structures
 */
Christoph Hellwig's avatar
Christoph Hellwig committed
1007
void iwl3945_hw_txq_ctx_free(struct iwl3945_priv *priv)
1008 1009 1010 1011 1012
{
	int txq_id;

	/* Tx queues */
	for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++)
Christoph Hellwig's avatar
Christoph Hellwig committed
1013
		iwl3945_tx_queue_free(priv, &priv->txq[txq_id]);
1014 1015
}

Christoph Hellwig's avatar
Christoph Hellwig committed
1016
void iwl3945_hw_txq_ctx_stop(struct iwl3945_priv *priv)
1017 1018 1019 1020 1021
{
	int queue;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
Christoph Hellwig's avatar
Christoph Hellwig committed
1022
	if (iwl3945_grab_nic_access(priv)) {
1023
		spin_unlock_irqrestore(&priv->lock, flags);
Christoph Hellwig's avatar
Christoph Hellwig committed
1024
		iwl3945_hw_txq_ctx_free(priv);
1025 1026 1027 1028
		return;
	}

	/* stop SCD */
Christoph Hellwig's avatar
Christoph Hellwig committed
1029
	iwl3945_write_prph(priv, ALM_SCD_MODE_REG, 0);
1030 1031 1032

	/* reset TFD queues */
	for (queue = TFD_QUEUE_MIN; queue < TFD_QUEUE_MAX; queue++) {
Christoph Hellwig's avatar
Christoph Hellwig committed
1033 1034
		iwl3945_write_direct32(priv, FH_TCSR_CONFIG(queue), 0x0);
		iwl3945_poll_direct_bit(priv, FH_TSSR_TX_STATUS,
1035 1036 1037 1038
				ALM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(queue),
				1000);
	}

Christoph Hellwig's avatar
Christoph Hellwig committed
1039
	iwl3945_release_nic_access(priv);
1040 1041
	spin_unlock_irqrestore(&priv->lock, flags);

Christoph Hellwig's avatar
Christoph Hellwig committed
1042
	iwl3945_hw_txq_ctx_free(priv);
1043 1044
}

Christoph Hellwig's avatar
Christoph Hellwig committed
1045
int iwl3945_hw_nic_stop_master(struct iwl3945_priv *priv)
1046 1047 1048 1049 1050 1051 1052 1053
{
	int rc = 0;
	u32 reg_val;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);

	/* set stop master bit */
Christoph Hellwig's avatar
Christoph Hellwig committed
1054
	iwl3945_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
1055

Christoph Hellwig's avatar
Christoph Hellwig committed
1056
	reg_val = iwl3945_read32(priv, CSR_GP_CNTRL);
1057 1058 1059 1060 1061 1062

	if (CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE ==
	    (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE))
		IWL_DEBUG_INFO("Card in power save, master is already "
			       "stopped\n");
	else {
Christoph Hellwig's avatar
Christoph Hellwig committed
1063
		rc = iwl3945_poll_bit(priv, CSR_RESET,
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
				  CSR_RESET_REG_FLAG_MASTER_DISABLED,
				  CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
		if (rc < 0) {
			spin_unlock_irqrestore(&priv->lock, flags);
			return rc;
		}
	}

	spin_unlock_irqrestore(&priv->lock, flags);
	IWL_DEBUG_INFO("stop master\n");

	return rc;
}

Christoph Hellwig's avatar
Christoph Hellwig committed
1078
int iwl3945_hw_nic_reset(struct iwl3945_priv *priv)
1079 1080 1081 1082
{
	int rc;
	unsigned long flags;

Christoph Hellwig's avatar
Christoph Hellwig committed
1083
	iwl3945_hw_nic_stop_master(priv);
1084 1085 1086

	spin_lock_irqsave(&priv->lock, flags);

Christoph Hellwig's avatar
Christoph Hellwig committed
1087
	iwl3945_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
1088

Christoph Hellwig's avatar
Christoph Hellwig committed
1089
	rc = iwl3945_poll_bit(priv, CSR_GP_CNTRL,
1090 1091 1092
			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);

Christoph Hellwig's avatar
Christoph Hellwig committed
1093
	rc = iwl3945_grab_nic_access(priv);
1094
	if (!rc) {
Christoph Hellwig's avatar
Christoph Hellwig committed
1095
		iwl3945_write_prph(priv, APMG_CLK_CTRL_REG,
1096 1097 1098 1099
					 APMG_CLK_VAL_BSM_CLK_RQT);

		udelay(10);

Christoph Hellwig's avatar
Christoph Hellwig committed
1100
		iwl3945_set_bit(priv, CSR_GP_CNTRL,
1101 1102
			    CSR_GP_CNTRL_REG_FLAG_INIT_DONE);

Christoph Hellwig's avatar
Christoph Hellwig committed
1103 1104
		iwl3945_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
		iwl3945_write_prph(priv, APMG_RTC_INT_STT_REG,
1105 1106 1107
					0xFFFFFFFF);

		/* enable DMA */
Christoph Hellwig's avatar
Christoph Hellwig committed
1108
		iwl3945_write_prph(priv, APMG_CLK_EN_REG,
1109 1110 1111 1112
					 APMG_CLK_VAL_DMA_CLK_RQT |
					 APMG_CLK_VAL_BSM_CLK_RQT);
		udelay(10);

Christoph Hellwig's avatar
Christoph Hellwig committed
1113
		iwl3945_set_bits_prph(priv, APMG_PS_CTRL_REG,
1114 1115
				APMG_PS_CTRL_VAL_RESET_REQ);
		udelay(5);
Christoph Hellwig's avatar
Christoph Hellwig committed
1116
		iwl3945_clear_bits_prph(priv, APMG_PS_CTRL_REG,
1117
				APMG_PS_CTRL_VAL_RESET_REQ);
Christoph Hellwig's avatar
Christoph Hellwig committed
1118
		iwl3945_release_nic_access(priv);
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
	}

	/* Clear the 'host command active' bit... */
	clear_bit(STATUS_HCMD_ACTIVE, &priv->status);

	wake_up_interruptible(&priv->wait_command_queue);
	spin_unlock_irqrestore(&priv->lock, flags);

	return rc;
}

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
1131
 * iwl3945_hw_reg_adjust_power_by_temp
1132 1133
 * return index delta into power gain settings table
*/
Christoph Hellwig's avatar
Christoph Hellwig committed
1134
static int iwl3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading)
1135 1136 1137 1138 1139
{
	return (new_reading - old_reading) * (-11) / 100;
}

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
1140
 * iwl3945_hw_reg_temp_out_of_range - Keep temperature in sane range
1141
 */
Christoph Hellwig's avatar
Christoph Hellwig committed
1142
static inline int iwl3945_hw_reg_temp_out_of_range(int temperature)
1143 1144 1145 1146
{
	return (((temperature < -260) || (temperature > 25)) ? 1 : 0);
}

Christoph Hellwig's avatar
Christoph Hellwig committed
1147
int iwl3945_hw_get_temperature(struct iwl3945_priv *priv)
1148
{
Christoph Hellwig's avatar
Christoph Hellwig committed
1149
	return iwl3945_read32(priv, CSR_UCODE_DRV_GP2);
1150 1151 1152
}

/**
Christoph Hellwig's avatar
Christoph Hellwig committed
1153
 * iwl3945_hw_reg_txpower_get_temperature
1154 1155
 * get the current temperature by reading from NIC
*/
Christoph Hellwig's avatar
Christoph Hellwig committed
1156
static int iwl3945_hw_reg_txpower_get_temperature(struct iwl3945_priv *priv)
1157 1158 1159
{
	int temperature;

Christoph Hellwig's avatar
Christoph Hellwig committed
1160
	temperature = iwl3945_hw_get_temperature(priv);
1161 1162 1163 1164 1165 1166

	/* driver's okay range is -260 to +25.
	 *   human readable okay range is 0 to +285 */
	IWL_DEBUG_INFO("Temperature: %d\n", temperature + IWL_TEMP_CONVERT);

	/* handle insane temp reading */
Christoph Hellwig's avatar
Christoph Hellwig committed
1167
	if (iwl3945_hw_reg_temp_out_of_range(temperature)) {
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
		IWL_ERROR("Error bad temperature value  %d\n", temperature);

		/* if really really hot(?),
		 *   substitute the 3rd band/group's temp measured at factory */
		if (priv->last_temperature > 100)
			temperature = priv->eeprom.groups[2].temperature;
		else /* else use most recent "sane" value from driver */
			temperature = priv->last_temperature;
	}

	return temperature;	/* raw, not "human readable" */
}

/* Adjust Txpower only if temperature variance is greater than threshold.
 *
 * Both are lower than older versions' 9 degrees */
#define IWL_TEMPERATURE_LIMIT_TIMER   6

/**
 * is_temp_calib_needed - determines if new calibration is needed
 *
 * records new temperature in tx_mgr->temperature.
 * replaces tx_mgr->last_temperature *only* if calib needed
 *    (assumes caller will actually do the calibration!). */
Christoph Hellwig's avatar
Christoph Hellwig committed
1192
static int is_temp_calib_needed(struct iwl3945_priv *priv)
1193 1194 1195
{
	int temp_diff;

Christoph Hellwig's avatar
Christoph Hellwig committed
1196
	priv->temperature = iwl3945_hw_reg_txpower_get_temperature(priv);
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206