blob: db5bfcb036ca274a2f1183e4f5e7ff963568998d [file] [log] [blame]
Mohamed Abbasab53d8a2008-03-25 16:33:36 -07001/******************************************************************************
2 *
Reinette Chatre1f447802010-01-15 13:43:41 -08003 * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
Mohamed Abbasab53d8a2008-03-25 16:33:36 -07004 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
17 *
18 * The full GNU General Public License is included in this distribution in the
19 * file called LICENSE.
20 *
21 * Contact Information:
Winkler, Tomas759ef892008-12-09 11:28:58 -080022 * Intel Linux Wireless <ilw@linux.intel.com>
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070023 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
24 *
25 *****************************************************************************/
26
27
28#include <linux/kernel.h>
29#include <linux/module.h>
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070030#include <linux/init.h>
31#include <linux/pci.h>
32#include <linux/dma-mapping.h>
33#include <linux/delay.h>
34#include <linux/skbuff.h>
35#include <linux/netdevice.h>
36#include <linux/wireless.h>
37#include <net/mac80211.h>
38#include <linux/etherdevice.h>
39#include <asm/unaligned.h>
40
Tomas Winkler3e0d4cb2008-04-24 11:55:38 -070041#include "iwl-dev.h"
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070042#include "iwl-core.h"
Tomas Winklerfee12472008-04-03 16:05:21 -070043#include "iwl-io.h"
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070044
Wey-Yi Guy02f5dac2009-09-17 10:43:54 -070045/* default: IWL_LED_BLINK(0) using blinking index table */
46static int led_mode;
47module_param(led_mode, int, S_IRUGO);
48MODULE_PARM_DESC(led_mode, "led mode: 0=blinking, 1=On(RF On)/Off(RF Off), "
Wey-Yi Guy6c69d122010-03-18 15:37:49 -070049 "(default 0)");
Wey-Yi Guy02f5dac2009-09-17 10:43:54 -070050
Tomas Winkler0eee6122008-07-11 11:53:36 +080051
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070052static const struct {
Wey-Yi Guye5108d02009-07-17 09:30:20 -070053 u16 tpt; /* Mb/s */
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070054 u8 on_time;
Tomas Winkler0eee6122008-07-11 11:53:36 +080055 u8 off_time;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070056} blink_tbl[] =
57{
58 {300, 25, 25},
59 {200, 40, 40},
60 {100, 55, 55},
61 {70, 65, 65},
62 {50, 75, 75},
63 {20, 85, 85},
Wey-Yi Guy85fecff2009-09-11 10:38:07 -070064 {10, 95, 95},
65 {5, 110, 110},
66 {1, 130, 130},
Tomas Winklerec1a7462008-07-11 11:53:37 +080067 {0, 167, 167},
Johannes Berge932a602009-10-02 13:44:03 -070068 /* SOLID_ON */
Tomas Winklerec1a7462008-07-11 11:53:37 +080069 {-1, IWL_LED_SOLID, 0}
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070070};
71
Tomas Winklerec1a7462008-07-11 11:53:37 +080072#define IWL_1MB_RATE (128 * 1024)
73#define IWL_LED_THRESHOLD (16)
74#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
75#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
76
Wey-Yi Guyf2d0d0e2009-09-11 10:38:14 -070077/*
78 * Adjust led blink rate to compensate on a MAC Clock difference on every HW
79 * Led blink rate analysis showed an average deviation of 0% on 3945,
80 * 5% on 4965 HW and 20% on 5000 series and up.
81 * Need to compensate on the led on/off time per HW according to the deviation
82 * to achieve the desired led frequency
83 * The calculation is: (100-averageDeviation)/100 * blinkTime
84 * For code efficiency the calculation will be:
85 * compensation = (100 - averageDeviation) * 64 / 100
86 * NewBlinkTime = (compensation * BlinkTime) / 64
87 */
88static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
89 u8 time, u16 compensation)
90{
91 if (!compensation) {
92 IWL_ERR(priv, "undefined blink compensation: "
93 "use pre-defined blinking time\n");
94 return time;
95 }
96
97 return (u8)((time * compensation) >> 6);
98}
99
Tomas Winklerec1a7462008-07-11 11:53:37 +0800100/* Set led pattern command */
Johannes Berge932a602009-10-02 13:44:03 -0700101static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700102{
Tomas Winklerec1a7462008-07-11 11:53:37 +0800103 struct iwl_led_cmd led_cmd = {
Johannes Berge932a602009-10-02 13:44:03 -0700104 .id = IWL_LED_LINK,
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700105 .interval = IWL_DEF_LED_INTRVL
106 };
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700107
Tomas Winklerec1a7462008-07-11 11:53:37 +0800108 BUG_ON(idx > IWL_MAX_BLINK_TBL);
109
Wey-Yi Guyf2d0d0e2009-09-11 10:38:14 -0700110 IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n",
111 priv->cfg->led_compensation);
112 led_cmd.on =
113 iwl_blink_compensation(priv, blink_tbl[idx].on_time,
114 priv->cfg->led_compensation);
115 led_cmd.off =
116 iwl_blink_compensation(priv, blink_tbl[idx].off_time,
117 priv->cfg->led_compensation);
Tomas Winklerec1a7462008-07-11 11:53:37 +0800118
Johannes Berge932a602009-10-02 13:44:03 -0700119 return priv->cfg->ops->led->cmd(priv, &led_cmd);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700120}
121
Johannes Berge932a602009-10-02 13:44:03 -0700122int iwl_led_start(struct iwl_priv *priv)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700123{
Johannes Berge932a602009-10-02 13:44:03 -0700124 return priv->cfg->ops->led->on(priv);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700125}
Johannes Berge932a602009-10-02 13:44:03 -0700126EXPORT_SYMBOL(iwl_led_start);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700127
Johannes Berge932a602009-10-02 13:44:03 -0700128int iwl_led_associate(struct iwl_priv *priv)
Esti Kummerc785d1d2008-07-18 13:53:07 +0800129{
Tomas Winklere1623442009-01-27 14:27:56 -0800130 IWL_DEBUG_LED(priv, "Associated\n");
Wey-Yi Guy02f5dac2009-09-17 10:43:54 -0700131 if (led_mode == IWL_LED_BLINK)
132 priv->allow_blinking = 1;
Johannes Berge932a602009-10-02 13:44:03 -0700133 priv->last_blink_time = jiffies;
134
135 return 0;
Esti Kummerc785d1d2008-07-18 13:53:07 +0800136}
Johannes Berge932a602009-10-02 13:44:03 -0700137
138int iwl_led_disassociate(struct iwl_priv *priv)
Esti Kummerc785d1d2008-07-18 13:53:07 +0800139{
140 priv->allow_blinking = 0;
Esti Kummerc785d1d2008-07-18 13:53:07 +0800141
142 return 0;
143}
144
145/*
Wey-Yi Guye5108d02009-07-17 09:30:20 -0700146 * calculate blink rate according to last second Tx/Rx activities
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700147 */
Tomas Winklerec1a7462008-07-11 11:53:37 +0800148static int iwl_get_blink_rate(struct iwl_priv *priv)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700149{
150 int i;
Wey-Yi Guye5108d02009-07-17 09:30:20 -0700151 /* count both tx and rx traffic to be able to
152 * handle traffic in either direction
153 */
Wey-Yi Guy22fdf3c2009-08-07 15:41:40 -0700154 u64 current_tpt = priv->tx_stats.data_bytes +
155 priv->rx_stats.data_bytes;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700156 s64 tpt = current_tpt - priv->led_tpt;
157
Tomas Winklera96a27f2008-10-23 23:48:56 -0700158 if (tpt < 0) /* wraparound */
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700159 tpt = -tpt;
160
Tomas Winklere1623442009-01-27 14:27:56 -0800161 IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
Andrew Morton03121102008-07-22 23:50:04 -0700162 (long long)tpt,
163 (unsigned long long)current_tpt);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700164 priv->led_tpt = current_tpt;
165
Tomas Winklerec1a7462008-07-11 11:53:37 +0800166 if (!priv->allow_blinking)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700167 i = IWL_MAX_BLINK_TBL;
Tomas Winklerec1a7462008-07-11 11:53:37 +0800168 else
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700169 for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
Johannes Berge932a602009-10-02 13:44:03 -0700170 if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700171 break;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700172
Tomas Winklere1623442009-01-27 14:27:56 -0800173 IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
Tomas Winklerec1a7462008-07-11 11:53:37 +0800174 return i;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700175}
176
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700177/*
178 * this function called from handler. Since setting Led command can
179 * happen very frequent we postpone led command to be called from
180 * REPLY handler so we know ucode is up
181 */
182void iwl_leds_background(struct iwl_priv *priv)
183{
Tomas Winklerec1a7462008-07-11 11:53:37 +0800184 u8 blink_idx;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700185
186 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
187 priv->last_blink_time = 0;
188 return;
189 }
Esti Kummerc785d1d2008-07-18 13:53:07 +0800190 if (iwl_is_rfkill(priv)) {
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700191 priv->last_blink_time = 0;
192 return;
193 }
194
195 if (!priv->allow_blinking) {
196 priv->last_blink_time = 0;
Tomas Winklerec1a7462008-07-11 11:53:37 +0800197 if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
198 priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
Johannes Berge932a602009-10-02 13:44:03 -0700199 iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700200 }
201 return;
202 }
203 if (!priv->last_blink_time ||
204 !time_after(jiffies, priv->last_blink_time +
205 msecs_to_jiffies(1000)))
206 return;
207
Tomas Winklerec1a7462008-07-11 11:53:37 +0800208 blink_idx = iwl_get_blink_rate(priv);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700209
210 /* call only if blink rate change */
Tomas Winklerec1a7462008-07-11 11:53:37 +0800211 if (blink_idx != priv->last_blink_rate)
Johannes Berge932a602009-10-02 13:44:03 -0700212 iwl_led_pattern(priv, blink_idx);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700213
Tomas Winkler0eee6122008-07-11 11:53:36 +0800214 priv->last_blink_time = jiffies;
Tomas Winklerec1a7462008-07-11 11:53:37 +0800215 priv->last_blink_rate = blink_idx;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700216}
Johannes Berge932a602009-10-02 13:44:03 -0700217EXPORT_SYMBOL(iwl_leds_background);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700218
Johannes Berge932a602009-10-02 13:44:03 -0700219void iwl_leds_init(struct iwl_priv *priv)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700220{
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700221 priv->last_blink_rate = 0;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700222 priv->last_blink_time = 0;
223 priv->allow_blinking = 0;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700224}
Johannes Berge932a602009-10-02 13:44:03 -0700225EXPORT_SYMBOL(iwl_leds_init);