1 /****************************************************************************** 2 * 3 * Copyright(c) 2003 - 2014 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 * The full GNU General Public License is included in this distribution in the 15 * file called LICENSE. 16 * 17 * Contact Information: 18 * Intel Linux Wireless <linuxwifi@intel.com> 19 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 20 * 21 *****************************************************************************/ 22 23 24 #include <linux/kernel.h> 25 #include <linux/module.h> 26 #include <linux/delay.h> 27 #include <linux/skbuff.h> 28 #include <linux/netdevice.h> 29 #include <net/mac80211.h> 30 #include <linux/etherdevice.h> 31 #include <asm/unaligned.h> 32 #include "iwl-io.h" 33 #include "iwl-trans.h" 34 #include "iwl-modparams.h" 35 #include "dev.h" 36 #include "agn.h" 37 38 /* Throughput OFF time(ms) ON time (ms) 39 * >300 25 25 40 * >200 to 300 40 40 41 * >100 to 200 55 55 42 * >70 to 100 65 65 43 * >50 to 70 75 75 44 * >20 to 50 85 85 45 * >10 to 20 95 95 46 * >5 to 10 110 110 47 * >1 to 5 130 130 48 * >0 to 1 167 167 49 * <=0 SOLID ON 50 */ 51 static const struct ieee80211_tpt_blink iwl_blink[] = { 52 { .throughput = 0, .blink_time = 334 }, 53 { .throughput = 1 * 1024 - 1, .blink_time = 260 }, 54 { .throughput = 5 * 1024 - 1, .blink_time = 220 }, 55 { .throughput = 10 * 1024 - 1, .blink_time = 190 }, 56 { .throughput = 20 * 1024 - 1, .blink_time = 170 }, 57 { .throughput = 50 * 1024 - 1, .blink_time = 150 }, 58 { .throughput = 70 * 1024 - 1, .blink_time = 130 }, 59 { .throughput = 100 * 1024 - 1, .blink_time = 110 }, 60 { .throughput = 200 * 1024 - 1, .blink_time = 80 }, 61 { .throughput = 300 * 1024 - 1, .blink_time = 50 }, 62 }; 63 64 /* Set led register off */ 65 void iwlagn_led_enable(struct iwl_priv *priv) 66 { 67 iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); 68 } 69 70 /* 71 * Adjust led blink rate to compensate on a MAC Clock difference on every HW 72 * Led blink rate analysis showed an average deviation of 20% on 5000 series 73 * and up. 74 * Need to compensate on the led on/off time per HW according to the deviation 75 * to achieve the desired led frequency 76 * The calculation is: (100-averageDeviation)/100 * blinkTime 77 * For code efficiency the calculation will be: 78 * compensation = (100 - averageDeviation) * 64 / 100 79 * NewBlinkTime = (compensation * BlinkTime) / 64 80 */ 81 static inline u8 iwl_blink_compensation(struct iwl_priv *priv, 82 u8 time, u16 compensation) 83 { 84 if (!compensation) { 85 IWL_ERR(priv, "undefined blink compensation: " 86 "use pre-defined blinking time\n"); 87 return time; 88 } 89 90 return (u8)((time * compensation) >> 6); 91 } 92 93 static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) 94 { 95 struct iwl_host_cmd cmd = { 96 .id = REPLY_LEDS_CMD, 97 .len = { sizeof(struct iwl_led_cmd), }, 98 .data = { led_cmd, }, 99 .flags = CMD_ASYNC, 100 }; 101 u32 reg; 102 103 reg = iwl_read32(priv->trans, CSR_LED_REG); 104 if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) 105 iwl_write32(priv->trans, CSR_LED_REG, 106 reg & CSR_LED_BSM_CTRL_MSK); 107 108 return iwl_dvm_send_cmd(priv, &cmd); 109 } 110 111 /* Set led pattern command */ 112 static int iwl_led_cmd(struct iwl_priv *priv, 113 unsigned long on, 114 unsigned long off) 115 { 116 struct iwl_led_cmd led_cmd = { 117 .id = IWL_LED_LINK, 118 .interval = IWL_DEF_LED_INTRVL 119 }; 120 int ret; 121 122 if (!test_bit(STATUS_READY, &priv->status)) 123 return -EBUSY; 124 125 if (priv->blink_on == on && priv->blink_off == off) 126 return 0; 127 128 if (off == 0) { 129 /* led is SOLID_ON */ 130 on = IWL_LED_SOLID; 131 } 132 133 led_cmd.on = iwl_blink_compensation(priv, on, 134 priv->cfg->base_params->led_compensation); 135 led_cmd.off = iwl_blink_compensation(priv, off, 136 priv->cfg->base_params->led_compensation); 137 138 ret = iwl_send_led_cmd(priv, &led_cmd); 139 if (!ret) { 140 priv->blink_on = on; 141 priv->blink_off = off; 142 } 143 return ret; 144 } 145 146 static void iwl_led_brightness_set(struct led_classdev *led_cdev, 147 enum led_brightness brightness) 148 { 149 struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); 150 unsigned long on = 0; 151 unsigned long off = 0; 152 153 if (brightness > 0) 154 on = IWL_LED_SOLID; 155 else 156 off = IWL_LED_SOLID; 157 158 iwl_led_cmd(priv, on, off); 159 } 160 161 static int iwl_led_blink_set(struct led_classdev *led_cdev, 162 unsigned long *delay_on, 163 unsigned long *delay_off) 164 { 165 struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); 166 167 return iwl_led_cmd(priv, *delay_on, *delay_off); 168 } 169 170 void iwl_leds_init(struct iwl_priv *priv) 171 { 172 int mode = iwlwifi_mod_params.led_mode; 173 int ret; 174 175 if (mode == IWL_LED_DISABLE) { 176 IWL_INFO(priv, "Led disabled\n"); 177 return; 178 } 179 if (mode == IWL_LED_DEFAULT) 180 mode = priv->cfg->led_mode; 181 182 priv->led.name = kasprintf(GFP_KERNEL, "%s-led", 183 wiphy_name(priv->hw->wiphy)); 184 priv->led.brightness_set = iwl_led_brightness_set; 185 priv->led.blink_set = iwl_led_blink_set; 186 priv->led.max_brightness = 1; 187 188 switch (mode) { 189 case IWL_LED_DEFAULT: 190 WARN_ON(1); 191 break; 192 case IWL_LED_BLINK: 193 priv->led.default_trigger = 194 ieee80211_create_tpt_led_trigger(priv->hw, 195 IEEE80211_TPT_LEDTRIG_FL_CONNECTED, 196 iwl_blink, ARRAY_SIZE(iwl_blink)); 197 break; 198 case IWL_LED_RF_STATE: 199 priv->led.default_trigger = 200 ieee80211_get_radio_led_name(priv->hw); 201 break; 202 } 203 204 ret = led_classdev_register(priv->trans->dev, &priv->led); 205 if (ret) { 206 kfree(priv->led.name); 207 return; 208 } 209 210 priv->led_registered = true; 211 } 212 213 void iwl_leds_exit(struct iwl_priv *priv) 214 { 215 if (!priv->led_registered) 216 return; 217 218 led_classdev_unregister(&priv->led); 219 kfree(priv->led.name); 220 } 221