1/****************************************************************************** 2 * 3 * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. 4 * 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: 22 * Intel Linux Wireless <ilw@linux.intel.com> 23 * 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> 30#include <linux/init.h> 31#include <linux/delay.h> 32#include <linux/skbuff.h> 33#include <linux/netdevice.h> 34#include <net/mac80211.h> 35#include <linux/etherdevice.h> 36#include <asm/unaligned.h> 37 38#include "iwl-dev.h" 39#include "iwl-core.h" 40#include "iwl-agn.h" 41#include "iwl-io.h" 42#include "iwl-trans.h" 43#include "iwl-shared.h" 44 45/* Throughput OFF time(ms) ON time (ms) 46 * >300 25 25 47 * >200 to 300 40 40 48 * >100 to 200 55 55 49 * >70 to 100 65 65 50 * >50 to 70 75 75 51 * >20 to 50 85 85 52 * >10 to 20 95 95 53 * >5 to 10 110 110 54 * >1 to 5 130 130 55 * >0 to 1 167 167 56 * <=0 SOLID ON 57 */ 58static const struct ieee80211_tpt_blink iwl_blink[] = { 59 { .throughput = 0, .blink_time = 334 }, 60 { .throughput = 1 * 1024 - 1, .blink_time = 260 }, 61 { .throughput = 5 * 1024 - 1, .blink_time = 220 }, 62 { .throughput = 10 * 1024 - 1, .blink_time = 190 }, 63 { .throughput = 20 * 1024 - 1, .blink_time = 170 }, 64 { .throughput = 50 * 1024 - 1, .blink_time = 150 }, 65 { .throughput = 70 * 1024 - 1, .blink_time = 130 }, 66 { .throughput = 100 * 1024 - 1, .blink_time = 110 }, 67 { .throughput = 200 * 1024 - 1, .blink_time = 80 }, 68 { .throughput = 300 * 1024 - 1, .blink_time = 50 }, 69}; 70 71/* Set led register off */ 72void iwlagn_led_enable(struct iwl_priv *priv) 73{ 74 iwl_write32(trans(priv), CSR_LED_REG, CSR_LED_REG_TRUN_ON); 75} 76 77/* 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 20% on 5000 series 80 * 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 100static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) 101{ 102 struct iwl_host_cmd cmd = { 103 .id = REPLY_LEDS_CMD, 104 .len = { sizeof(struct iwl_led_cmd), }, 105 .data = { led_cmd, }, 106 .flags = CMD_ASYNC, 107 }; 108 u32 reg; 109 110 reg = iwl_read32(trans(priv), CSR_LED_REG); 111 if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) 112 iwl_write32(trans(priv), CSR_LED_REG, 113 reg & CSR_LED_BSM_CTRL_MSK); 114 115 return iwl_dvm_send_cmd(priv, &cmd); 116} 117 118/* Set led pattern command */ 119static int iwl_led_cmd(struct iwl_priv *priv, 120 unsigned long on, 121 unsigned long off) 122{ 123 struct iwl_led_cmd led_cmd = { 124 .id = IWL_LED_LINK, 125 .interval = IWL_DEF_LED_INTRVL 126 }; 127 int ret; 128 129 if (!test_bit(STATUS_READY, &priv->status)) 130 return -EBUSY; 131 132 if (priv->blink_on == on && priv->blink_off == off) 133 return 0; 134 135 if (off == 0) { 136 /* led is SOLID_ON */ 137 on = IWL_LED_SOLID; 138 } 139 140 IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", 141 cfg(priv)->base_params->led_compensation); 142 led_cmd.on = iwl_blink_compensation(priv, on, 143 cfg(priv)->base_params->led_compensation); 144 led_cmd.off = iwl_blink_compensation(priv, off, 145 cfg(priv)->base_params->led_compensation); 146 147 ret = iwl_send_led_cmd(priv, &led_cmd); 148 if (!ret) { 149 priv->blink_on = on; 150 priv->blink_off = off; 151 } 152 return ret; 153} 154 155static void iwl_led_brightness_set(struct led_classdev *led_cdev, 156 enum led_brightness brightness) 157{ 158 struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); 159 unsigned long on = 0; 160 161 if (brightness > 0) 162 on = IWL_LED_SOLID; 163 164 iwl_led_cmd(priv, on, 0); 165} 166 167static int iwl_led_blink_set(struct led_classdev *led_cdev, 168 unsigned long *delay_on, 169 unsigned long *delay_off) 170{ 171 struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); 172 173 return iwl_led_cmd(priv, *delay_on, *delay_off); 174} 175 176void iwl_leds_init(struct iwl_priv *priv) 177{ 178 int mode = iwlagn_mod_params.led_mode; 179 int ret; 180 181 if (mode == IWL_LED_DISABLE) { 182 IWL_INFO(priv, "Led disabled\n"); 183 return; 184 } 185 if (mode == IWL_LED_DEFAULT) 186 mode = cfg(priv)->led_mode; 187 188 priv->led.name = kasprintf(GFP_KERNEL, "%s-led", 189 wiphy_name(priv->hw->wiphy)); 190 priv->led.brightness_set = iwl_led_brightness_set; 191 priv->led.blink_set = iwl_led_blink_set; 192 priv->led.max_brightness = 1; 193 194 switch (mode) { 195 case IWL_LED_DEFAULT: 196 WARN_ON(1); 197 break; 198 case IWL_LED_BLINK: 199 priv->led.default_trigger = 200 ieee80211_create_tpt_led_trigger(priv->hw, 201 IEEE80211_TPT_LEDTRIG_FL_CONNECTED, 202 iwl_blink, ARRAY_SIZE(iwl_blink)); 203 break; 204 case IWL_LED_RF_STATE: 205 priv->led.default_trigger = 206 ieee80211_get_radio_led_name(priv->hw); 207 break; 208 } 209 210 ret = led_classdev_register(trans(priv)->dev, &priv->led); 211 if (ret) { 212 kfree(priv->led.name); 213 return; 214 } 215 216 priv->led_registered = true; 217} 218 219void iwl_leds_exit(struct iwl_priv *priv) 220{ 221 if (!priv->led_registered) 222 return; 223 224 led_classdev_unregister(&priv->led); 225 kfree(priv->led.name); 226} 227