1*bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*bfcc09ddSBjoern A. Zeeb /*
3*bfcc09ddSBjoern A. Zeeb * Copyright (C) 2012-2014, 2018-2019 Intel Corporation
4*bfcc09ddSBjoern A. Zeeb * Copyright (C) 2017 Intel Deutschland GmbH
5*bfcc09ddSBjoern A. Zeeb */
6*bfcc09ddSBjoern A. Zeeb #include <linux/leds.h>
7*bfcc09ddSBjoern A. Zeeb #include "iwl-io.h"
8*bfcc09ddSBjoern A. Zeeb #include "iwl-csr.h"
9*bfcc09ddSBjoern A. Zeeb #include "mvm.h"
10*bfcc09ddSBjoern A. Zeeb
iwl_mvm_send_led_fw_cmd(struct iwl_mvm * mvm,bool on)11*bfcc09ddSBjoern A. Zeeb static void iwl_mvm_send_led_fw_cmd(struct iwl_mvm *mvm, bool on)
12*bfcc09ddSBjoern A. Zeeb {
13*bfcc09ddSBjoern A. Zeeb struct iwl_led_cmd led_cmd = {
14*bfcc09ddSBjoern A. Zeeb .status = cpu_to_le32(on),
15*bfcc09ddSBjoern A. Zeeb };
16*bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd cmd = {
17*bfcc09ddSBjoern A. Zeeb .id = WIDE_ID(LONG_GROUP, LEDS_CMD),
18*bfcc09ddSBjoern A. Zeeb .len = { sizeof(led_cmd), },
19*bfcc09ddSBjoern A. Zeeb .data = { &led_cmd, },
20*bfcc09ddSBjoern A. Zeeb .flags = CMD_ASYNC,
21*bfcc09ddSBjoern A. Zeeb };
22*bfcc09ddSBjoern A. Zeeb int err;
23*bfcc09ddSBjoern A. Zeeb
24*bfcc09ddSBjoern A. Zeeb if (!iwl_mvm_firmware_running(mvm))
25*bfcc09ddSBjoern A. Zeeb return;
26*bfcc09ddSBjoern A. Zeeb
27*bfcc09ddSBjoern A. Zeeb err = iwl_mvm_send_cmd(mvm, &cmd);
28*bfcc09ddSBjoern A. Zeeb
29*bfcc09ddSBjoern A. Zeeb if (err)
30*bfcc09ddSBjoern A. Zeeb IWL_WARN(mvm, "LED command failed: %d\n", err);
31*bfcc09ddSBjoern A. Zeeb }
32*bfcc09ddSBjoern A. Zeeb
iwl_mvm_led_set(struct iwl_mvm * mvm,bool on)33*bfcc09ddSBjoern A. Zeeb static void iwl_mvm_led_set(struct iwl_mvm *mvm, bool on)
34*bfcc09ddSBjoern A. Zeeb {
35*bfcc09ddSBjoern A. Zeeb if (fw_has_capa(&mvm->fw->ucode_capa,
36*bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT)) {
37*bfcc09ddSBjoern A. Zeeb iwl_mvm_send_led_fw_cmd(mvm, on);
38*bfcc09ddSBjoern A. Zeeb return;
39*bfcc09ddSBjoern A. Zeeb }
40*bfcc09ddSBjoern A. Zeeb
41*bfcc09ddSBjoern A. Zeeb iwl_write32(mvm->trans, CSR_LED_REG,
42*bfcc09ddSBjoern A. Zeeb on ? CSR_LED_REG_TURN_ON : CSR_LED_REG_TURN_OFF);
43*bfcc09ddSBjoern A. Zeeb }
44*bfcc09ddSBjoern A. Zeeb
iwl_led_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)45*bfcc09ddSBjoern A. Zeeb static void iwl_led_brightness_set(struct led_classdev *led_cdev,
46*bfcc09ddSBjoern A. Zeeb enum led_brightness brightness)
47*bfcc09ddSBjoern A. Zeeb {
48*bfcc09ddSBjoern A. Zeeb struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led);
49*bfcc09ddSBjoern A. Zeeb
50*bfcc09ddSBjoern A. Zeeb iwl_mvm_led_set(mvm, brightness > 0);
51*bfcc09ddSBjoern A. Zeeb }
52*bfcc09ddSBjoern A. Zeeb
iwl_mvm_leds_init(struct iwl_mvm * mvm)53*bfcc09ddSBjoern A. Zeeb int iwl_mvm_leds_init(struct iwl_mvm *mvm)
54*bfcc09ddSBjoern A. Zeeb {
55*bfcc09ddSBjoern A. Zeeb int mode = iwlwifi_mod_params.led_mode;
56*bfcc09ddSBjoern A. Zeeb int ret;
57*bfcc09ddSBjoern A. Zeeb
58*bfcc09ddSBjoern A. Zeeb switch (mode) {
59*bfcc09ddSBjoern A. Zeeb case IWL_LED_BLINK:
60*bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Blink led mode not supported, used default\n");
61*bfcc09ddSBjoern A. Zeeb fallthrough;
62*bfcc09ddSBjoern A. Zeeb case IWL_LED_DEFAULT:
63*bfcc09ddSBjoern A. Zeeb case IWL_LED_RF_STATE:
64*bfcc09ddSBjoern A. Zeeb mode = IWL_LED_RF_STATE;
65*bfcc09ddSBjoern A. Zeeb break;
66*bfcc09ddSBjoern A. Zeeb case IWL_LED_DISABLE:
67*bfcc09ddSBjoern A. Zeeb IWL_INFO(mvm, "Led disabled\n");
68*bfcc09ddSBjoern A. Zeeb return 0;
69*bfcc09ddSBjoern A. Zeeb default:
70*bfcc09ddSBjoern A. Zeeb return -EINVAL;
71*bfcc09ddSBjoern A. Zeeb }
72*bfcc09ddSBjoern A. Zeeb
73*bfcc09ddSBjoern A. Zeeb mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
74*bfcc09ddSBjoern A. Zeeb wiphy_name(mvm->hw->wiphy));
75*bfcc09ddSBjoern A. Zeeb if (!mvm->led.name)
76*bfcc09ddSBjoern A. Zeeb return -ENOMEM;
77*bfcc09ddSBjoern A. Zeeb
78*bfcc09ddSBjoern A. Zeeb mvm->led.brightness_set = iwl_led_brightness_set;
79*bfcc09ddSBjoern A. Zeeb mvm->led.max_brightness = 1;
80*bfcc09ddSBjoern A. Zeeb
81*bfcc09ddSBjoern A. Zeeb if (mode == IWL_LED_RF_STATE)
82*bfcc09ddSBjoern A. Zeeb mvm->led.default_trigger =
83*bfcc09ddSBjoern A. Zeeb ieee80211_get_radio_led_name(mvm->hw);
84*bfcc09ddSBjoern A. Zeeb
85*bfcc09ddSBjoern A. Zeeb ret = led_classdev_register(mvm->trans->dev, &mvm->led);
86*bfcc09ddSBjoern A. Zeeb if (ret) {
87*bfcc09ddSBjoern A. Zeeb kfree(mvm->led.name);
88*bfcc09ddSBjoern A. Zeeb IWL_INFO(mvm, "Failed to enable led\n");
89*bfcc09ddSBjoern A. Zeeb return ret;
90*bfcc09ddSBjoern A. Zeeb }
91*bfcc09ddSBjoern A. Zeeb
92*bfcc09ddSBjoern A. Zeeb mvm->init_status |= IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE;
93*bfcc09ddSBjoern A. Zeeb return 0;
94*bfcc09ddSBjoern A. Zeeb }
95*bfcc09ddSBjoern A. Zeeb
iwl_mvm_leds_sync(struct iwl_mvm * mvm)96*bfcc09ddSBjoern A. Zeeb void iwl_mvm_leds_sync(struct iwl_mvm *mvm)
97*bfcc09ddSBjoern A. Zeeb {
98*bfcc09ddSBjoern A. Zeeb if (!(mvm->init_status & IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE))
99*bfcc09ddSBjoern A. Zeeb return;
100*bfcc09ddSBjoern A. Zeeb
101*bfcc09ddSBjoern A. Zeeb /*
102*bfcc09ddSBjoern A. Zeeb * if we control through the register, we're doing it
103*bfcc09ddSBjoern A. Zeeb * even when the firmware isn't up, so no need to sync
104*bfcc09ddSBjoern A. Zeeb */
105*bfcc09ddSBjoern A. Zeeb if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000)
106*bfcc09ddSBjoern A. Zeeb return;
107*bfcc09ddSBjoern A. Zeeb
108*bfcc09ddSBjoern A. Zeeb iwl_mvm_led_set(mvm, mvm->led.brightness > 0);
109*bfcc09ddSBjoern A. Zeeb }
110*bfcc09ddSBjoern A. Zeeb
iwl_mvm_leds_exit(struct iwl_mvm * mvm)111*bfcc09ddSBjoern A. Zeeb void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
112*bfcc09ddSBjoern A. Zeeb {
113*bfcc09ddSBjoern A. Zeeb if (!(mvm->init_status & IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE))
114*bfcc09ddSBjoern A. Zeeb return;
115*bfcc09ddSBjoern A. Zeeb
116*bfcc09ddSBjoern A. Zeeb led_classdev_unregister(&mvm->led);
117*bfcc09ddSBjoern A. Zeeb kfree(mvm->led.name);
118*bfcc09ddSBjoern A. Zeeb mvm->init_status &= ~IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE;
119*bfcc09ddSBjoern A. Zeeb }
120