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