blob: 46ccdf406e8e781318760e40a3a5cae54d2992ed [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);
Wey-Yi Guye88735d2010-12-11 08:46:56 -080048MODULE_PARM_DESC(led_mode, "0=system default, "
Wey-Yi Guy564b3442010-11-09 09:21:34 -080049 "1=On(RF On)/Off(RF Off), 2=blinking");
Tomas Winkler0eee6122008-07-11 11:53:36 +080050
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070051static const struct {
Wey-Yi Guye5108d02009-07-17 09:30:20 -070052 u16 tpt; /* Mb/s */
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070053 u8 on_time;
Tomas Winkler0eee6122008-07-11 11:53:36 +080054 u8 off_time;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070055} blink_tbl[] =
56{
57 {300, 25, 25},
58 {200, 40, 40},
59 {100, 55, 55},
60 {70, 65, 65},
61 {50, 75, 75},
62 {20, 85, 85},
Wey-Yi Guy85fecff2009-09-11 10:38:07 -070063 {10, 95, 95},
64 {5, 110, 110},
65 {1, 130, 130},
Tomas Winklerec1a7462008-07-11 11:53:37 +080066 {0, 167, 167},
Johannes Berge932a602009-10-02 13:44:03 -070067 /* SOLID_ON */
Tomas Winklerec1a7462008-07-11 11:53:37 +080068 {-1, IWL_LED_SOLID, 0}
Mohamed Abbasab53d8a2008-03-25 16:33:36 -070069};
70
Tomas Winklerec1a7462008-07-11 11:53:37 +080071#define IWL_1MB_RATE (128 * 1024)
72#define IWL_LED_THRESHOLD (16)
73#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
74#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
75
Wey-Yi Guyf2d0d0e2009-09-11 10:38:14 -070076/*
77 * Adjust led blink rate to compensate on a MAC Clock difference on every HW
78 * Led blink rate analysis showed an average deviation of 0% on 3945,
79 * 5% on 4965 HW and 20% on 5000 series and up.
80 * Need to compensate on the led on/off time per HW according to the deviation
81 * to achieve the desired led frequency
82 * The calculation is: (100-averageDeviation)/100 * blinkTime
83 * For code efficiency the calculation will be:
84 * compensation = (100 - averageDeviation) * 64 / 100
85 * NewBlinkTime = (compensation * BlinkTime) / 64
86 */
87static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
88 u8 time, u16 compensation)
89{
90 if (!compensation) {
91 IWL_ERR(priv, "undefined blink compensation: "
92 "use pre-defined blinking time\n");
93 return time;
94 }
95
96 return (u8)((time * compensation) >> 6);
97}
98
Tomas Winklerec1a7462008-07-11 11:53:37 +080099/* Set led pattern command */
Johannes Berge932a602009-10-02 13:44:03 -0700100static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700101{
Tomas Winklerec1a7462008-07-11 11:53:37 +0800102 struct iwl_led_cmd led_cmd = {
Johannes Berge932a602009-10-02 13:44:03 -0700103 .id = IWL_LED_LINK,
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700104 .interval = IWL_DEF_LED_INTRVL
105 };
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700106
Tomas Winklerec1a7462008-07-11 11:53:37 +0800107 BUG_ON(idx > IWL_MAX_BLINK_TBL);
108
Wey-Yi Guyf2d0d0e2009-09-11 10:38:14 -0700109 IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n",
Wey-Yi Guy7cb1b082010-10-06 08:10:00 -0700110 priv->cfg->base_params->led_compensation);
Wey-Yi Guyf2d0d0e2009-09-11 10:38:14 -0700111 led_cmd.on =
112 iwl_blink_compensation(priv, blink_tbl[idx].on_time,
Wey-Yi Guy7cb1b082010-10-06 08:10:00 -0700113 priv->cfg->base_params->led_compensation);
Wey-Yi Guyf2d0d0e2009-09-11 10:38:14 -0700114 led_cmd.off =
115 iwl_blink_compensation(priv, blink_tbl[idx].off_time,
Wey-Yi Guy7cb1b082010-10-06 08:10:00 -0700116 priv->cfg->base_params->led_compensation);
Tomas Winklerec1a7462008-07-11 11:53:37 +0800117
Johannes Berge932a602009-10-02 13:44:03 -0700118 return priv->cfg->ops->led->cmd(priv, &led_cmd);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700119}
120
Johannes Berge932a602009-10-02 13:44:03 -0700121int iwl_led_start(struct iwl_priv *priv)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700122{
Johannes Berge932a602009-10-02 13:44:03 -0700123 return priv->cfg->ops->led->on(priv);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700124}
Johannes Berge932a602009-10-02 13:44:03 -0700125EXPORT_SYMBOL(iwl_led_start);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700126
Johannes Berge932a602009-10-02 13:44:03 -0700127int iwl_led_associate(struct iwl_priv *priv)
Esti Kummerc785d1d2008-07-18 13:53:07 +0800128{
Tomas Winklere1623442009-01-27 14:27:56 -0800129 IWL_DEBUG_LED(priv, "Associated\n");
Wey-Yi Guy564b3442010-11-09 09:21:34 -0800130 if (priv->cfg->led_mode == IWL_LED_BLINK)
Wey-Yi Guy02f5dac2009-09-17 10:43:54 -0700131 priv->allow_blinking = 1;
Johannes Berge932a602009-10-02 13:44:03 -0700132 priv->last_blink_time = jiffies;
133
134 return 0;
Esti Kummerc785d1d2008-07-18 13:53:07 +0800135}
Johannes Berg2295c662010-10-23 09:15:41 -0700136EXPORT_SYMBOL(iwl_led_associate);
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}
Johannes Berg2295c662010-10-23 09:15:41 -0700144EXPORT_SYMBOL(iwl_led_disassociate);
Esti Kummerc785d1d2008-07-18 13:53:07 +0800145
146/*
Wey-Yi Guye5108d02009-07-17 09:30:20 -0700147 * calculate blink rate according to last second Tx/Rx activities
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700148 */
Tomas Winklerec1a7462008-07-11 11:53:37 +0800149static int iwl_get_blink_rate(struct iwl_priv *priv)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700150{
151 int i;
Wey-Yi Guye5108d02009-07-17 09:30:20 -0700152 /* count both tx and rx traffic to be able to
153 * handle traffic in either direction
154 */
Wey-Yi Guy22fdf3c2009-08-07 15:41:40 -0700155 u64 current_tpt = priv->tx_stats.data_bytes +
156 priv->rx_stats.data_bytes;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700157 s64 tpt = current_tpt - priv->led_tpt;
158
Tomas Winklera96a27f2008-10-23 23:48:56 -0700159 if (tpt < 0) /* wraparound */
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700160 tpt = -tpt;
161
Tomas Winklere1623442009-01-27 14:27:56 -0800162 IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
Andrew Morton03121102008-07-22 23:50:04 -0700163 (long long)tpt,
164 (unsigned long long)current_tpt);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700165 priv->led_tpt = current_tpt;
166
Tomas Winklerec1a7462008-07-11 11:53:37 +0800167 if (!priv->allow_blinking)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700168 i = IWL_MAX_BLINK_TBL;
Tomas Winklerec1a7462008-07-11 11:53:37 +0800169 else
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700170 for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
Johannes Berge932a602009-10-02 13:44:03 -0700171 if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700172 break;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700173
Tomas Winklere1623442009-01-27 14:27:56 -0800174 IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
Tomas Winklerec1a7462008-07-11 11:53:37 +0800175 return i;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700176}
177
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700178/*
179 * this function called from handler. Since setting Led command can
180 * happen very frequent we postpone led command to be called from
181 * REPLY handler so we know ucode is up
182 */
183void iwl_leds_background(struct iwl_priv *priv)
184{
Tomas Winklerec1a7462008-07-11 11:53:37 +0800185 u8 blink_idx;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700186
187 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
188 priv->last_blink_time = 0;
189 return;
190 }
Esti Kummerc785d1d2008-07-18 13:53:07 +0800191 if (iwl_is_rfkill(priv)) {
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700192 priv->last_blink_time = 0;
193 return;
194 }
195
196 if (!priv->allow_blinking) {
197 priv->last_blink_time = 0;
Tomas Winklerec1a7462008-07-11 11:53:37 +0800198 if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
199 priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
Johannes Berge932a602009-10-02 13:44:03 -0700200 iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700201 }
202 return;
203 }
204 if (!priv->last_blink_time ||
205 !time_after(jiffies, priv->last_blink_time +
206 msecs_to_jiffies(1000)))
207 return;
208
Tomas Winklerec1a7462008-07-11 11:53:37 +0800209 blink_idx = iwl_get_blink_rate(priv);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700210
211 /* call only if blink rate change */
Tomas Winklerec1a7462008-07-11 11:53:37 +0800212 if (blink_idx != priv->last_blink_rate)
Johannes Berge932a602009-10-02 13:44:03 -0700213 iwl_led_pattern(priv, blink_idx);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700214
Tomas Winkler0eee6122008-07-11 11:53:36 +0800215 priv->last_blink_time = jiffies;
Tomas Winklerec1a7462008-07-11 11:53:37 +0800216 priv->last_blink_rate = blink_idx;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700217}
Johannes Berge932a602009-10-02 13:44:03 -0700218EXPORT_SYMBOL(iwl_leds_background);
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700219
Johannes Berge932a602009-10-02 13:44:03 -0700220void iwl_leds_init(struct iwl_priv *priv)
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700221{
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700222 priv->last_blink_rate = 0;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700223 priv->last_blink_time = 0;
224 priv->allow_blinking = 0;
Wey-Yi Guy564b3442010-11-09 09:21:34 -0800225 if (led_mode != IWL_LED_DEFAULT &&
226 led_mode != priv->cfg->led_mode)
227 priv->cfg->led_mode = led_mode;
Mohamed Abbasab53d8a2008-03-25 16:33:36 -0700228}
Johannes Berge932a602009-10-02 13:44:03 -0700229EXPORT_SYMBOL(iwl_leds_init);