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