diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-led.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-led.c | 253 |
1 files changed, 128 insertions, 125 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index db5bfcb036ca..7c23beb49d7c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /****************************************************************************** | 1 | /****************************************************************************** |
2 | * | 2 | * |
3 | * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. | 3 | * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | 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 | 6 | * under the terms of version 2 of the GNU General Public License as |
@@ -45,39 +45,45 @@ | |||
45 | /* default: IWL_LED_BLINK(0) using blinking index table */ | 45 | /* default: IWL_LED_BLINK(0) using blinking index table */ |
46 | static int led_mode; | 46 | static int led_mode; |
47 | module_param(led_mode, int, S_IRUGO); | 47 | module_param(led_mode, int, S_IRUGO); |
48 | MODULE_PARM_DESC(led_mode, "led mode: 0=blinking, 1=On(RF On)/Off(RF Off), " | 48 | MODULE_PARM_DESC(led_mode, "0=system default, " |
49 | "(default 0)"); | 49 | "1=On(RF On)/Off(RF Off), 2=blinking"); |
50 | 50 | ||
51 | 51 | /* Throughput OFF time(ms) ON time (ms) | |
52 | static const struct { | 52 | * >300 25 25 |
53 | u16 tpt; /* Mb/s */ | 53 | * >200 to 300 40 40 |
54 | u8 on_time; | 54 | * >100 to 200 55 55 |
55 | u8 off_time; | 55 | * >70 to 100 65 65 |
56 | } blink_tbl[] = | 56 | * >50 to 70 75 75 |
57 | { | 57 | * >20 to 50 85 85 |
58 | {300, 25, 25}, | 58 | * >10 to 20 95 95 |
59 | {200, 40, 40}, | 59 | * >5 to 10 110 110 |
60 | {100, 55, 55}, | 60 | * >1 to 5 130 130 |
61 | {70, 65, 65}, | 61 | * >0 to 1 167 167 |
62 | {50, 75, 75}, | 62 | * <=0 SOLID ON |
63 | {20, 85, 85}, | 63 | */ |
64 | {10, 95, 95}, | 64 | static const struct ieee80211_tpt_blink iwl_blink[] = { |
65 | {5, 110, 110}, | 65 | { .throughput = 0, .blink_time = 334 }, |
66 | {1, 130, 130}, | 66 | { .throughput = 1 * 1024 - 1, .blink_time = 260 }, |
67 | {0, 167, 167}, | 67 | { .throughput = 5 * 1024 - 1, .blink_time = 220 }, |
68 | /* SOLID_ON */ | 68 | { .throughput = 10 * 1024 - 1, .blink_time = 190 }, |
69 | {-1, IWL_LED_SOLID, 0} | 69 | { .throughput = 20 * 1024 - 1, .blink_time = 170 }, |
70 | { .throughput = 50 * 1024 - 1, .blink_time = 150 }, | ||
71 | { .throughput = 70 * 1024 - 1, .blink_time = 130 }, | ||
72 | { .throughput = 100 * 1024 - 1, .blink_time = 110 }, | ||
73 | { .throughput = 200 * 1024 - 1, .blink_time = 80 }, | ||
74 | { .throughput = 300 * 1024 - 1, .blink_time = 50 }, | ||
70 | }; | 75 | }; |
71 | 76 | ||
72 | #define IWL_1MB_RATE (128 * 1024) | 77 | /* Set led register off */ |
73 | #define IWL_LED_THRESHOLD (16) | 78 | void iwlagn_led_enable(struct iwl_priv *priv) |
74 | #define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */ | 79 | { |
75 | #define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1) | 80 | iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON); |
81 | } | ||
76 | 82 | ||
77 | /* | 83 | /* |
78 | * Adjust led blink rate to compensate on a MAC Clock difference on every HW | 84 | * 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, | 85 | * Led blink rate analysis showed an average deviation of 20% on 5000 series |
80 | * 5% on 4965 HW and 20% on 5000 series and up. | 86 | * and up. |
81 | * Need to compensate on the led on/off time per HW according to the deviation | 87 | * Need to compensate on the led on/off time per HW according to the deviation |
82 | * to achieve the desired led frequency | 88 | * to achieve the desired led frequency |
83 | * The calculation is: (100-averageDeviation)/100 * blinkTime | 89 | * The calculation is: (100-averageDeviation)/100 * blinkTime |
@@ -97,129 +103,126 @@ static inline u8 iwl_blink_compensation(struct iwl_priv *priv, | |||
97 | return (u8)((time * compensation) >> 6); | 103 | return (u8)((time * compensation) >> 6); |
98 | } | 104 | } |
99 | 105 | ||
106 | static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) | ||
107 | { | ||
108 | struct iwl_host_cmd cmd = { | ||
109 | .id = REPLY_LEDS_CMD, | ||
110 | .len = { sizeof(struct iwl_led_cmd), }, | ||
111 | .data = { led_cmd, }, | ||
112 | .flags = CMD_ASYNC, | ||
113 | .callback = NULL, | ||
114 | }; | ||
115 | u32 reg; | ||
116 | |||
117 | reg = iwl_read32(priv, CSR_LED_REG); | ||
118 | if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) | ||
119 | iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); | ||
120 | |||
121 | return iwl_send_cmd(priv, &cmd); | ||
122 | } | ||
123 | |||
100 | /* Set led pattern command */ | 124 | /* Set led pattern command */ |
101 | static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx) | 125 | static int iwl_led_cmd(struct iwl_priv *priv, |
126 | unsigned long on, | ||
127 | unsigned long off) | ||
102 | { | 128 | { |
103 | struct iwl_led_cmd led_cmd = { | 129 | struct iwl_led_cmd led_cmd = { |
104 | .id = IWL_LED_LINK, | 130 | .id = IWL_LED_LINK, |
105 | .interval = IWL_DEF_LED_INTRVL | 131 | .interval = IWL_DEF_LED_INTRVL |
106 | }; | 132 | }; |
133 | int ret; | ||
107 | 134 | ||
108 | BUG_ON(idx > IWL_MAX_BLINK_TBL); | 135 | if (!test_bit(STATUS_READY, &priv->status)) |
136 | return -EBUSY; | ||
109 | 137 | ||
110 | IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n", | 138 | if (priv->blink_on == on && priv->blink_off == off) |
111 | priv->cfg->led_compensation); | 139 | return 0; |
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); | ||
118 | 140 | ||
119 | return priv->cfg->ops->led->cmd(priv, &led_cmd); | 141 | if (off == 0) { |
120 | } | 142 | /* led is SOLID_ON */ |
143 | on = IWL_LED_SOLID; | ||
144 | } | ||
121 | 145 | ||
122 | int iwl_led_start(struct iwl_priv *priv) | 146 | IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", |
123 | { | 147 | priv->cfg->base_params->led_compensation); |
124 | return priv->cfg->ops->led->on(priv); | 148 | led_cmd.on = iwl_blink_compensation(priv, on, |
149 | priv->cfg->base_params->led_compensation); | ||
150 | led_cmd.off = iwl_blink_compensation(priv, off, | ||
151 | priv->cfg->base_params->led_compensation); | ||
152 | |||
153 | ret = iwl_send_led_cmd(priv, &led_cmd); | ||
154 | if (!ret) { | ||
155 | priv->blink_on = on; | ||
156 | priv->blink_off = off; | ||
157 | } | ||
158 | return ret; | ||
125 | } | 159 | } |
126 | EXPORT_SYMBOL(iwl_led_start); | ||
127 | 160 | ||
128 | int iwl_led_associate(struct iwl_priv *priv) | 161 | static void iwl_led_brightness_set(struct led_classdev *led_cdev, |
162 | enum led_brightness brightness) | ||
129 | { | 163 | { |
130 | IWL_DEBUG_LED(priv, "Associated\n"); | 164 | struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); |
131 | if (led_mode == IWL_LED_BLINK) | 165 | unsigned long on = 0; |
132 | priv->allow_blinking = 1; | ||
133 | priv->last_blink_time = jiffies; | ||
134 | 166 | ||
135 | return 0; | 167 | if (brightness > 0) |
136 | } | 168 | on = IWL_LED_SOLID; |
137 | 169 | ||
138 | int iwl_led_disassociate(struct iwl_priv *priv) | 170 | iwl_led_cmd(priv, on, 0); |
139 | { | ||
140 | priv->allow_blinking = 0; | ||
141 | |||
142 | return 0; | ||
143 | } | 171 | } |
144 | 172 | ||
145 | /* | 173 | static int iwl_led_blink_set(struct led_classdev *led_cdev, |
146 | * calculate blink rate according to last second Tx/Rx activities | 174 | unsigned long *delay_on, |
147 | */ | 175 | unsigned long *delay_off) |
148 | static int iwl_get_blink_rate(struct iwl_priv *priv) | ||
149 | { | 176 | { |
150 | int i; | 177 | struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); |
151 | /* count both tx and rx traffic to be able to | 178 | |
152 | * handle traffic in either direction | 179 | return iwl_led_cmd(priv, *delay_on, *delay_off); |
153 | */ | ||
154 | u64 current_tpt = priv->tx_stats.data_bytes + | ||
155 | priv->rx_stats.data_bytes; | ||
156 | s64 tpt = current_tpt - priv->led_tpt; | ||
157 | |||
158 | if (tpt < 0) /* wraparound */ | ||
159 | tpt = -tpt; | ||
160 | |||
161 | IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n", | ||
162 | (long long)tpt, | ||
163 | (unsigned long long)current_tpt); | ||
164 | priv->led_tpt = current_tpt; | ||
165 | |||
166 | if (!priv->allow_blinking) | ||
167 | i = IWL_MAX_BLINK_TBL; | ||
168 | else | ||
169 | for (i = 0; i < IWL_MAX_BLINK_TBL; i++) | ||
170 | if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE)) | ||
171 | break; | ||
172 | |||
173 | IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i); | ||
174 | return i; | ||
175 | } | 180 | } |
176 | 181 | ||
177 | /* | 182 | void iwl_leds_init(struct iwl_priv *priv) |
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 | */ | ||
182 | void iwl_leds_background(struct iwl_priv *priv) | ||
183 | { | 183 | { |
184 | u8 blink_idx; | 184 | int mode = led_mode; |
185 | 185 | int ret; | |
186 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { | 186 | |
187 | priv->last_blink_time = 0; | 187 | if (mode == IWL_LED_DEFAULT) |
188 | return; | 188 | mode = priv->cfg->led_mode; |
189 | } | 189 | |
190 | if (iwl_is_rfkill(priv)) { | 190 | priv->led.name = kasprintf(GFP_KERNEL, "%s-led", |
191 | priv->last_blink_time = 0; | 191 | wiphy_name(priv->hw->wiphy)); |
192 | return; | 192 | priv->led.brightness_set = iwl_led_brightness_set; |
193 | priv->led.blink_set = iwl_led_blink_set; | ||
194 | priv->led.max_brightness = 1; | ||
195 | |||
196 | switch (mode) { | ||
197 | case IWL_LED_DEFAULT: | ||
198 | WARN_ON(1); | ||
199 | break; | ||
200 | case IWL_LED_BLINK: | ||
201 | priv->led.default_trigger = | ||
202 | ieee80211_create_tpt_led_trigger(priv->hw, | ||
203 | IEEE80211_TPT_LEDTRIG_FL_CONNECTED, | ||
204 | iwl_blink, ARRAY_SIZE(iwl_blink)); | ||
205 | break; | ||
206 | case IWL_LED_RF_STATE: | ||
207 | priv->led.default_trigger = | ||
208 | ieee80211_get_radio_led_name(priv->hw); | ||
209 | break; | ||
193 | } | 210 | } |
194 | 211 | ||
195 | if (!priv->allow_blinking) { | 212 | ret = led_classdev_register(&priv->pci_dev->dev, &priv->led); |
196 | priv->last_blink_time = 0; | 213 | if (ret) { |
197 | if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) { | 214 | kfree(priv->led.name); |
198 | priv->last_blink_rate = IWL_SOLID_BLINK_IDX; | ||
199 | iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX); | ||
200 | } | ||
201 | return; | 215 | return; |
202 | } | 216 | } |
203 | if (!priv->last_blink_time || | ||
204 | !time_after(jiffies, priv->last_blink_time + | ||
205 | msecs_to_jiffies(1000))) | ||
206 | return; | ||
207 | |||
208 | blink_idx = iwl_get_blink_rate(priv); | ||
209 | 217 | ||
210 | /* call only if blink rate change */ | 218 | priv->led_registered = true; |
211 | if (blink_idx != priv->last_blink_rate) | ||
212 | iwl_led_pattern(priv, blink_idx); | ||
213 | |||
214 | priv->last_blink_time = jiffies; | ||
215 | priv->last_blink_rate = blink_idx; | ||
216 | } | 219 | } |
217 | EXPORT_SYMBOL(iwl_leds_background); | ||
218 | 220 | ||
219 | void iwl_leds_init(struct iwl_priv *priv) | 221 | void iwl_leds_exit(struct iwl_priv *priv) |
220 | { | 222 | { |
221 | priv->last_blink_rate = 0; | 223 | if (!priv->led_registered) |
222 | priv->last_blink_time = 0; | 224 | return; |
223 | priv->allow_blinking = 0; | 225 | |
226 | led_classdev_unregister(&priv->led); | ||
227 | kfree(priv->led.name); | ||
224 | } | 228 | } |
225 | EXPORT_SYMBOL(iwl_leds_init); | ||