18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact Information: 88c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 98c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci *****************************************************************************/ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 198c2ecf20Sopenharmony_ci#include <net/mac80211.h> 208c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 218c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 228c2ecf20Sopenharmony_ci#include "iwl-io.h" 238c2ecf20Sopenharmony_ci#include "iwl-trans.h" 248c2ecf20Sopenharmony_ci#include "iwl-modparams.h" 258c2ecf20Sopenharmony_ci#include "dev.h" 268c2ecf20Sopenharmony_ci#include "agn.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Throughput OFF time(ms) ON time (ms) 298c2ecf20Sopenharmony_ci * >300 25 25 308c2ecf20Sopenharmony_ci * >200 to 300 40 40 318c2ecf20Sopenharmony_ci * >100 to 200 55 55 328c2ecf20Sopenharmony_ci * >70 to 100 65 65 338c2ecf20Sopenharmony_ci * >50 to 70 75 75 348c2ecf20Sopenharmony_ci * >20 to 50 85 85 358c2ecf20Sopenharmony_ci * >10 to 20 95 95 368c2ecf20Sopenharmony_ci * >5 to 10 110 110 378c2ecf20Sopenharmony_ci * >1 to 5 130 130 388c2ecf20Sopenharmony_ci * >0 to 1 167 167 398c2ecf20Sopenharmony_ci * <=0 SOLID ON 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistatic const struct ieee80211_tpt_blink iwl_blink[] = { 428c2ecf20Sopenharmony_ci { .throughput = 0, .blink_time = 334 }, 438c2ecf20Sopenharmony_ci { .throughput = 1 * 1024 - 1, .blink_time = 260 }, 448c2ecf20Sopenharmony_ci { .throughput = 5 * 1024 - 1, .blink_time = 220 }, 458c2ecf20Sopenharmony_ci { .throughput = 10 * 1024 - 1, .blink_time = 190 }, 468c2ecf20Sopenharmony_ci { .throughput = 20 * 1024 - 1, .blink_time = 170 }, 478c2ecf20Sopenharmony_ci { .throughput = 50 * 1024 - 1, .blink_time = 150 }, 488c2ecf20Sopenharmony_ci { .throughput = 70 * 1024 - 1, .blink_time = 130 }, 498c2ecf20Sopenharmony_ci { .throughput = 100 * 1024 - 1, .blink_time = 110 }, 508c2ecf20Sopenharmony_ci { .throughput = 200 * 1024 - 1, .blink_time = 80 }, 518c2ecf20Sopenharmony_ci { .throughput = 300 * 1024 - 1, .blink_time = 50 }, 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Set led register off */ 558c2ecf20Sopenharmony_civoid iwlagn_led_enable(struct iwl_priv *priv) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Adjust led blink rate to compensate on a MAC Clock difference on every HW 628c2ecf20Sopenharmony_ci * Led blink rate analysis showed an average deviation of 20% on 5000 series 638c2ecf20Sopenharmony_ci * and up. 648c2ecf20Sopenharmony_ci * Need to compensate on the led on/off time per HW according to the deviation 658c2ecf20Sopenharmony_ci * to achieve the desired led frequency 668c2ecf20Sopenharmony_ci * The calculation is: (100-averageDeviation)/100 * blinkTime 678c2ecf20Sopenharmony_ci * For code efficiency the calculation will be: 688c2ecf20Sopenharmony_ci * compensation = (100 - averageDeviation) * 64 / 100 698c2ecf20Sopenharmony_ci * NewBlinkTime = (compensation * BlinkTime) / 64 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_cistatic inline u8 iwl_blink_compensation(struct iwl_priv *priv, 728c2ecf20Sopenharmony_ci u8 time, u16 compensation) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci if (!compensation) { 758c2ecf20Sopenharmony_ci IWL_ERR(priv, "undefined blink compensation: " 768c2ecf20Sopenharmony_ci "use pre-defined blinking time\n"); 778c2ecf20Sopenharmony_ci return time; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return (u8)((time * compensation) >> 6); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 868c2ecf20Sopenharmony_ci .id = REPLY_LEDS_CMD, 878c2ecf20Sopenharmony_ci .len = { sizeof(struct iwl_led_cmd), }, 888c2ecf20Sopenharmony_ci .data = { led_cmd, }, 898c2ecf20Sopenharmony_ci .flags = CMD_ASYNC, 908c2ecf20Sopenharmony_ci }; 918c2ecf20Sopenharmony_ci u32 reg; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci reg = iwl_read32(priv->trans, CSR_LED_REG); 948c2ecf20Sopenharmony_ci if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) 958c2ecf20Sopenharmony_ci iwl_write32(priv->trans, CSR_LED_REG, 968c2ecf20Sopenharmony_ci reg & CSR_LED_BSM_CTRL_MSK); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* Set led pattern command */ 1028c2ecf20Sopenharmony_cistatic int iwl_led_cmd(struct iwl_priv *priv, 1038c2ecf20Sopenharmony_ci unsigned long on, 1048c2ecf20Sopenharmony_ci unsigned long off) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct iwl_led_cmd led_cmd = { 1078c2ecf20Sopenharmony_ci .id = IWL_LED_LINK, 1088c2ecf20Sopenharmony_ci .interval = IWL_DEF_LED_INTRVL 1098c2ecf20Sopenharmony_ci }; 1108c2ecf20Sopenharmony_ci int ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!test_bit(STATUS_READY, &priv->status)) 1138c2ecf20Sopenharmony_ci return -EBUSY; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (priv->blink_on == on && priv->blink_off == off) 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (off == 0) { 1198c2ecf20Sopenharmony_ci /* led is SOLID_ON */ 1208c2ecf20Sopenharmony_ci on = IWL_LED_SOLID; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci led_cmd.on = iwl_blink_compensation(priv, on, 1248c2ecf20Sopenharmony_ci priv->trans->trans_cfg->base_params->led_compensation); 1258c2ecf20Sopenharmony_ci led_cmd.off = iwl_blink_compensation(priv, off, 1268c2ecf20Sopenharmony_ci priv->trans->trans_cfg->base_params->led_compensation); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ret = iwl_send_led_cmd(priv, &led_cmd); 1298c2ecf20Sopenharmony_ci if (!ret) { 1308c2ecf20Sopenharmony_ci priv->blink_on = on; 1318c2ecf20Sopenharmony_ci priv->blink_off = off; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void iwl_led_brightness_set(struct led_classdev *led_cdev, 1378c2ecf20Sopenharmony_ci enum led_brightness brightness) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); 1408c2ecf20Sopenharmony_ci unsigned long on = 0; 1418c2ecf20Sopenharmony_ci unsigned long off = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (brightness > 0) 1448c2ecf20Sopenharmony_ci on = IWL_LED_SOLID; 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci off = IWL_LED_SOLID; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci iwl_led_cmd(priv, on, off); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int iwl_led_blink_set(struct led_classdev *led_cdev, 1528c2ecf20Sopenharmony_ci unsigned long *delay_on, 1538c2ecf20Sopenharmony_ci unsigned long *delay_off) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return iwl_led_cmd(priv, *delay_on, *delay_off); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_civoid iwl_leds_init(struct iwl_priv *priv) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int mode = iwlwifi_mod_params.led_mode; 1638c2ecf20Sopenharmony_ci int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (mode == IWL_LED_DISABLE) { 1668c2ecf20Sopenharmony_ci IWL_INFO(priv, "Led disabled\n"); 1678c2ecf20Sopenharmony_ci return; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci if (mode == IWL_LED_DEFAULT) 1708c2ecf20Sopenharmony_ci mode = priv->cfg->led_mode; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci priv->led.name = kasprintf(GFP_KERNEL, "%s-led", 1738c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy)); 1748c2ecf20Sopenharmony_ci if (!priv->led.name) 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci priv->led.brightness_set = iwl_led_brightness_set; 1788c2ecf20Sopenharmony_ci priv->led.blink_set = iwl_led_blink_set; 1798c2ecf20Sopenharmony_ci priv->led.max_brightness = 1; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci switch (mode) { 1828c2ecf20Sopenharmony_ci case IWL_LED_DEFAULT: 1838c2ecf20Sopenharmony_ci WARN_ON(1); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case IWL_LED_BLINK: 1868c2ecf20Sopenharmony_ci priv->led.default_trigger = 1878c2ecf20Sopenharmony_ci ieee80211_create_tpt_led_trigger(priv->hw, 1888c2ecf20Sopenharmony_ci IEEE80211_TPT_LEDTRIG_FL_CONNECTED, 1898c2ecf20Sopenharmony_ci iwl_blink, ARRAY_SIZE(iwl_blink)); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case IWL_LED_RF_STATE: 1928c2ecf20Sopenharmony_ci priv->led.default_trigger = 1938c2ecf20Sopenharmony_ci ieee80211_get_radio_led_name(priv->hw); 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = led_classdev_register(priv->trans->dev, &priv->led); 1988c2ecf20Sopenharmony_ci if (ret) { 1998c2ecf20Sopenharmony_ci kfree(priv->led.name); 2008c2ecf20Sopenharmony_ci return; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci priv->led_registered = true; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_civoid iwl_leds_exit(struct iwl_priv *priv) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (!priv->led_registered) 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci led_classdev_unregister(&priv->led); 2128c2ecf20Sopenharmony_ci kfree(priv->led.name); 2138c2ecf20Sopenharmony_ci} 214