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