^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Huawei WMI laptop extras driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2018 Ayman Bagabas <ayman.bagabas@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/acpi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/debugfs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/dmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/input.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/input/sparse-keymap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/power_supply.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/sysfs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/wmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <acpi/battery.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * Huawei WMI GUIDs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) /* Legacy GUIDs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) /* HWMI commands */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) BATTERY_THRESH_GET = 0x00001103, /* \GBTT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) BATTERY_THRESH_SET = 0x00001003, /* \SBTT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) FN_LOCK_GET = 0x00000604, /* \GFRS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) FN_LOCK_SET = 0x00000704, /* \SFRS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) MICMUTE_LED_SET = 0x00000b04, /* \SMLS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) union hwmi_arg {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) u64 cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) u8 args[8];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) struct quirk_entry {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) bool battery_reset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) bool ec_micmute;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) bool report_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static struct quirk_entry *quirks;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) struct huawei_wmi_debug {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) struct dentry *root;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) u64 arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct huawei_wmi {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) bool battery_available;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) bool fn_lock_available;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) struct huawei_wmi_debug debug;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) struct input_dev *idev[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) struct led_classdev cdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) struct device *dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) struct mutex wmi_lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) static struct huawei_wmi *huawei_wmi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) static const struct key_entry huawei_wmi_keymap[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) { KE_KEY, 0x281, { KEY_BRIGHTNESSDOWN } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) { KE_KEY, 0x282, { KEY_BRIGHTNESSUP } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) { KE_KEY, 0x284, { KEY_MUTE } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) { KE_KEY, 0x285, { KEY_VOLUMEDOWN } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) { KE_KEY, 0x286, { KEY_VOLUMEUP } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) { KE_KEY, 0x287, { KEY_MICMUTE } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) { KE_KEY, 0x289, { KEY_WLAN } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) // Huawei |M| key
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) { KE_KEY, 0x28a, { KEY_CONFIG } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) // Keyboard backlit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) { KE_END, 0 }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static int battery_reset = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static int report_brightness = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) module_param(battery_reset, bint, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) MODULE_PARM_DESC(battery_reset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) "Reset battery charge values to (0-0) before disabling it using (0-100)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) module_param(report_brightness, bint, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) MODULE_PARM_DESC(report_brightness,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) "Report brightness keys.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) /* Quirks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static int __init dmi_matched(const struct dmi_system_id *dmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) quirks = dmi->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) static struct quirk_entry quirk_unknown = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) static struct quirk_entry quirk_battery_reset = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) .battery_reset = true,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static struct quirk_entry quirk_matebook_x = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) .ec_micmute = true,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) .report_brightness = true,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static const struct dmi_system_id huawei_quirks[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .ident = "Huawei MACH-WX9",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) .driver_data = &quirk_battery_reset
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .ident = "Huawei MateBook X",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) .driver_data = &quirk_matebook_x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) /* Utils */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) static int huawei_wmi_call(struct huawei_wmi *huawei,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) struct acpi_buffer *in, struct acpi_buffer *out)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) mutex_lock(&huawei->wmi_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) mutex_unlock(&huawei->wmi_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (ACPI_FAILURE(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) dev_err(huawei->dev, "Failed to evaluate wmi method\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) /* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) * the remaining 0x100 sized buffer has the return status of every call. In case
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) * the return status is non-zero, we return -ENODEV but still copy the returned
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) * buffer to the given buffer parameter (buf).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) struct huawei_wmi *huawei = huawei_wmi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) struct acpi_buffer in;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) union acpi_object *obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) size_t len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) int err, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) in.length = sizeof(arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) in.pointer = &arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) /* Some models require calling HWMI twice to execute a command. We evaluate
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) * HWMI and if we get a non-zero return status we evaluate it again.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) for (i = 0; i < 2; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) err = huawei_wmi_call(huawei, &in, &out);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) goto fail_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) obj = out.pointer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) if (!obj) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) err = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) goto fail_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) switch (obj->type) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) /* Models that implement both "legacy" and HWMI tend to return a 0x104
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) * sized buffer instead of a package of 0x4 and 0x100 buffers.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) case ACPI_TYPE_BUFFER:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) if (obj->buffer.length == 0x104) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) // Skip the first 4 bytes.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) obj->buffer.pointer += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) len = 0x100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) err = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) goto fail_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) /* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) * other is 256 bytes.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) case ACPI_TYPE_PACKAGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) if (obj->package.count != 2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) err = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) goto fail_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) obj = &obj->package.elements[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) if (obj->type != ACPI_TYPE_BUFFER) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) err = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) goto fail_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) len = obj->buffer.length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) /* Shouldn't get here! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) err = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) goto fail_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) if (!*obj->buffer.pointer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) err = (*obj->buffer.pointer) ? -ENODEV : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) if (buf) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) len = min(buflen, len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) memcpy(buf, obj->buffer.pointer, len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) fail_cmd:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) kfree(out.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) /* LEDs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) enum led_brightness brightness)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) /* This is a workaround until the "legacy" interface is implemented. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) if (quirks && quirks->ec_micmute) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) char *acpi_method;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) acpi_handle handle;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) union acpi_object args[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) struct acpi_object_list arg_list = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) .pointer = args,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) .count = ARRAY_SIZE(args),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) handle = ec_get_handle();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) if (!handle)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) args[1].integer.value = 0x04;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) if (acpi_has_method(handle, "SPIN")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) acpi_method = "SPIN";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) args[0].integer.value = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) args[2].integer.value = brightness ? 1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) } else if (acpi_has_method(handle, "WPIN")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) acpi_method = "WPIN";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) args[0].integer.value = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) args[2].integer.value = brightness ? 0 : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) union hwmi_arg arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) arg.cmd = MICMUTE_LED_SET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) arg.args[2] = brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) return huawei_wmi_cmd(arg.cmd, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) static void huawei_wmi_leds_setup(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) huawei->cdev.name = "platform::micmute";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) huawei->cdev.max_brightness = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) huawei->cdev.default_trigger = "audio-micmute";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) huawei->cdev.dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) devm_led_classdev_register(dev, &huawei->cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) /* Battery protection */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) static int huawei_wmi_battery_get(int *start, int *end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) u8 ret[0x100];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) int err, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) /* Find the last two non-zero values. Return status is ignored. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) i = 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) if (start)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) *start = ret[i-1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) if (end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) *end = ret[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) } while (i > 2 && !ret[i--]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) static int huawei_wmi_battery_set(int start, int end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) union hwmi_arg arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) if (start < 0 || end < 0 || start > 100 || end > 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) arg.cmd = BATTERY_THRESH_SET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) arg.args[2] = start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) arg.args[3] = end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) /* This is an edge case were some models turn battery protection
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) * off without changing their thresholds values. We clear the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) * values before turning off protection. Sometimes we need a sleep delay to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) * make sure these values make their way to EC memory.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) if (quirks && quirks->battery_reset && start == 0 && end == 100) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) err = huawei_wmi_battery_set(0, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) msleep(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) err = huawei_wmi_cmd(arg.cmd, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) static ssize_t charge_control_start_threshold_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) int err, start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) err = huawei_wmi_battery_get(&start, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) return sprintf(buf, "%d\n", start);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) static ssize_t charge_control_end_threshold_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) int err, end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) err = huawei_wmi_battery_get(NULL, &end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) return sprintf(buf, "%d\n", end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) static ssize_t charge_control_thresholds_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) int err, start, end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) err = huawei_wmi_battery_get(&start, &end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) return sprintf(buf, "%d %d\n", start, end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) static ssize_t charge_control_start_threshold_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) const char *buf, size_t size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) int err, start, end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) err = huawei_wmi_battery_get(NULL, &end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) if (sscanf(buf, "%d", &start) != 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) err = huawei_wmi_battery_set(start, end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) return size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) static ssize_t charge_control_end_threshold_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) const char *buf, size_t size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) int err, start, end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) err = huawei_wmi_battery_get(&start, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) if (sscanf(buf, "%d", &end) != 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) err = huawei_wmi_battery_set(start, end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) return size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) static ssize_t charge_control_thresholds_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) const char *buf, size_t size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) int err, start, end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) if (sscanf(buf, "%d %d", &start, &end) != 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) err = huawei_wmi_battery_set(start, end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) return size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) static DEVICE_ATTR_RW(charge_control_start_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) static DEVICE_ATTR_RW(charge_control_end_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) static DEVICE_ATTR_RW(charge_control_thresholds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) static int huawei_wmi_battery_add(struct power_supply *battery)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) int err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) err = device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) err = device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) static int huawei_wmi_battery_remove(struct power_supply *battery)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) static struct acpi_battery_hook huawei_wmi_battery_hook = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) .add_battery = huawei_wmi_battery_add,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) .remove_battery = huawei_wmi_battery_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) .name = "Huawei Battery Extension"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) static void huawei_wmi_battery_setup(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) huawei->battery_available = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) if (huawei_wmi_battery_get(NULL, NULL)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) huawei->battery_available = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) battery_hook_register(&huawei_wmi_battery_hook);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) device_create_file(dev, &dev_attr_charge_control_thresholds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) static void huawei_wmi_battery_exit(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) if (huawei->battery_available) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) battery_hook_unregister(&huawei_wmi_battery_hook);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) device_remove_file(dev, &dev_attr_charge_control_thresholds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) /* Fn lock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) static int huawei_wmi_fn_lock_get(int *on)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) u8 ret[0x100] = { 0 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) int err, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) /* Find the first non-zero value. Return status is ignored. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) i = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) if (on)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) *on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) } while (i < 0xff && !ret[i++]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) static int huawei_wmi_fn_lock_set(int on)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) union hwmi_arg arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) arg.cmd = FN_LOCK_SET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) return huawei_wmi_cmd(arg.cmd, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) static ssize_t fn_lock_state_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) int err, on;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) err = huawei_wmi_fn_lock_get(&on);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) return sprintf(buf, "%d\n", on);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) static ssize_t fn_lock_state_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) const char *buf, size_t size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) int on, err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) if (kstrtoint(buf, 10, &on) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) on < 0 || on > 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) err = huawei_wmi_fn_lock_set(on);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) return size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) static DEVICE_ATTR_RW(fn_lock_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) static void huawei_wmi_fn_lock_setup(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) huawei->fn_lock_available = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) if (huawei_wmi_fn_lock_get(NULL)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) huawei->fn_lock_available = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) device_create_file(dev, &dev_attr_fn_lock_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) static void huawei_wmi_fn_lock_exit(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) if (huawei->fn_lock_available)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) device_remove_file(dev, &dev_attr_fn_lock_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) /* debugfs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) union acpi_object *obj)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) struct huawei_wmi *huawei = m->private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) switch (obj->type) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) case ACPI_TYPE_INTEGER:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) seq_printf(m, "0x%llx", obj->integer.value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) case ACPI_TYPE_STRING:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) case ACPI_TYPE_BUFFER:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) seq_puts(m, "{");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) for (i = 0; i < obj->buffer.length; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) if (i < obj->buffer.length - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) seq_puts(m, ",");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) seq_puts(m, "}");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) case ACPI_TYPE_PACKAGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) seq_puts(m, "[");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) for (i = 0; i < obj->package.count; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) if (i < obj->package.count - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) seq_puts(m, ",");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) seq_puts(m, "]");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) struct huawei_wmi *huawei = m->private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) struct acpi_buffer in;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) union acpi_object *obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) in.length = sizeof(u64);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) in.pointer = &huawei->debug.arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) err = huawei_wmi_call(huawei, &in, &out);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) obj = out.pointer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) if (!obj) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) err = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) goto fail_debugfs_call;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) huawei_wmi_debugfs_call_dump(m, huawei, obj);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) fail_debugfs_call:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) kfree(out.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) static void huawei_wmi_debugfs_setup(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) debugfs_create_x64("arg", 0644, huawei->debug.root,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) &huawei->debug.arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) debugfs_create_file("call", 0400,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) static void huawei_wmi_debugfs_exit(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) struct huawei_wmi *huawei = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) debugfs_remove_recursive(huawei->debug.root);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) /* Input */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) static void huawei_wmi_process_key(struct input_dev *idev, int code)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) const struct key_entry *key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) * WMI0 uses code 0x80 to indicate a hotkey event.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) * The actual key is fetched from the method WQ00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) * using WMI0_EXPENSIVE_GUID.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) if (code == 0x80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) union acpi_object *obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) status = wmi_query_block(WMI0_EXPENSIVE_GUID, 0, &response);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) obj = (union acpi_object *)response.pointer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) if (obj && obj->type == ACPI_TYPE_INTEGER)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) code = obj->integer.value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) kfree(response.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) key = sparse_keymap_entry_from_scancode(idev, code);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) if (!key) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) if (quirks && !quirks->report_brightness &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) (key->sw.code == KEY_BRIGHTNESSDOWN ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) key->sw.code == KEY_BRIGHTNESSUP))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) sparse_keymap_report_entry(idev, key, 1, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) static void huawei_wmi_input_notify(u32 value, void *context)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) struct input_dev *idev = (struct input_dev *)context;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) union acpi_object *obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) status = wmi_get_event_data(value, &response);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) if (ACPI_FAILURE(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) dev_err(&idev->dev, "Unable to get event data\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) obj = (union acpi_object *)response.pointer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) if (obj && obj->type == ACPI_TYPE_INTEGER)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) huawei_wmi_process_key(idev, obj->integer.value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) dev_err(&idev->dev, "Bad response type\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) kfree(response.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) static int huawei_wmi_input_setup(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) const char *guid,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) struct input_dev **idev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) *idev = devm_input_allocate_device(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) if (!*idev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) (*idev)->name = "Huawei WMI hotkeys";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) (*idev)->phys = "wmi/input0";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) (*idev)->id.bustype = BUS_HOST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) (*idev)->dev.parent = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) input_register_device(*idev) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) wmi_install_notify_handler(guid, huawei_wmi_input_notify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) *idev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) static void huawei_wmi_input_exit(struct device *dev, const char *guid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) wmi_remove_notify_handler(guid);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) /* Huawei driver */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) static const struct wmi_device_id huawei_wmi_events_id_table[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) { .guid_string = WMI0_EVENT_GUID },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) { .guid_string = HWMI_EVENT_GUID },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) static int huawei_wmi_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) const struct wmi_device_id *guid = huawei_wmi_events_id_table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) platform_set_drvdata(pdev, huawei_wmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) huawei_wmi->dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) while (*guid->guid_string) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) struct input_dev *idev = *huawei_wmi->idev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) if (wmi_has_guid(guid->guid_string)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) idev++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) guid++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814) if (wmi_has_guid(HWMI_METHOD_GUID)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) mutex_init(&huawei_wmi->wmi_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) huawei_wmi_leds_setup(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) huawei_wmi_fn_lock_setup(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) huawei_wmi_battery_setup(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820) huawei_wmi_debugfs_setup(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826) static int huawei_wmi_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) const struct wmi_device_id *guid = huawei_wmi_events_id_table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) while (*guid->guid_string) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831) if (wmi_has_guid(guid->guid_string))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832) huawei_wmi_input_exit(&pdev->dev, guid->guid_string);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834) guid++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) if (wmi_has_guid(HWMI_METHOD_GUID)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) huawei_wmi_debugfs_exit(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) huawei_wmi_battery_exit(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840) huawei_wmi_fn_lock_exit(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) static struct platform_driver huawei_wmi_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) .name = "huawei-wmi",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) .probe = huawei_wmi_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) .remove = huawei_wmi_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854) static __init int huawei_wmi_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856) struct platform_device *pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) if (!huawei_wmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) quirks = &quirk_unknown;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) dmi_check_system(huawei_quirks);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 865) if (battery_reset != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 866) quirks->battery_reset = battery_reset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 867) if (report_brightness != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 868) quirks->report_brightness = report_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 869)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 870) err = platform_driver_register(&huawei_wmi_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 871) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 872) goto pdrv_err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 873)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 874) pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 875) if (IS_ERR(pdev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 876) err = PTR_ERR(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 877) goto pdev_err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 878) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 879)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 880) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 881)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 882) pdev_err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 883) platform_driver_unregister(&huawei_wmi_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 884) pdrv_err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 885) kfree(huawei_wmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 886) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 887) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 888)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 889) static __exit void huawei_wmi_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 890) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 891) struct platform_device *pdev = to_platform_device(huawei_wmi->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 892)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 893) platform_device_unregister(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 894) platform_driver_unregister(&huawei_wmi_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 895)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 896) kfree(huawei_wmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 897) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 898)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 899) module_init(huawei_wmi_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 900) module_exit(huawei_wmi_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 901)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 902) MODULE_ALIAS("wmi:"HWMI_METHOD_GUID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 903) MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 904) MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 905) MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 906) MODULE_LICENSE("GPL v2");