[PATCH v4 13/13] platform/x86: asus-wmi: Do not disable keyboard backlight on unloading

2019-05-14 Thread Yurii Pavlovskyi
The keyboard backlight is automatically disabled when the module is
unloaded as it is exposed as a ledclass device. Change this behavior to
ignore setting brightness when the device is in unloading state.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index feb8d72fc3c5..0c330d6a5871 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -471,6 +471,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, 
int value)
 static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
 {
+   /* Prevent disabling keyboard backlight on module unregister */
+   if (led_cdev->flags & LED_UNREGISTERING)
+   return;
+
do_kbd_led_set(led_cdev, value);
 }
 
-- 
2.17.1



[PATCH v4 12/13] platform/x86: asus-wmi: Switch fan boost mode

2019-05-14 Thread Yurii Pavlovskyi
The WMI exposes a write-only device ID where up to three fan modes can be
switched on some laptops (TUF Gaming FX505GM). There is a hotkey
combination Fn-F5 that does have a fan icon, which is designed to toggle
between fan modes. The DSTS of the device ID returns information about the
presence of this capability and the presence of each of the two additional
fan modes as a bitmask (0x01 - overboost present, 0x02 - silent present)
[1].

Add a SysFS entry that reads the last written value and updates value in
WMI on write and a hotkey handler that toggles the modes taking into
account their availability according to DSTS.

Modes:
* 0x00 - normal or balanced,
* 0x01 - overboost, increased fan RPM,
* 0x02 - silent, decreased fan RPM

[1] Link: https://lkml.org/lkml/2019/4/12/110

Signed-off-by: Yurii Pavlovskyi 
Suggested-by: Daniel Drake 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  10 ++
 drivers/platform/x86/asus-wmi.c   | 151 +-
 include/linux/platform_data/x86/asus-wmi.h|   1 +
 3 files changed, 154 insertions(+), 8 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..87ae5cc983bf 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,13 @@ KernelVersion:   3.5
 Contact:   "AceLan Kao" 
 Description:
Resume on lid open. 1 means on, 0 means off.
+
+What:  /sys/devices/platform//fan_mode
+Date:  Apr 2019
+KernelVersion: 5.2
+Contact:       "Yurii Pavlovskyi" 
+Description:
+   Fan boost mode:
+   * 0 - normal,
+   * 1 - overboost,
+   * 2 - silent
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ffb4e2530ea4..feb8d72fc3c5 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -70,6 +70,7 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_KBD_BRTUP   0xc4
 #define NOTIFY_KBD_BRTDWN  0xc5
 #define NOTIFY_KBD_BRTTOGGLE   0xc7
+#define NOTIFY_KBD_FBM 0x99
 
 #define ASUS_WMI_FNLOCK_BIOS_DISABLED  BIT(0)
 
@@ -80,6 +81,13 @@ MODULE_LICENSE("GPL");
 #define ASUS_FAN_CTRL_MANUAL   1
 #define ASUS_FAN_CTRL_AUTO 2
 
+#define ASUS_FAN_MODE_NORMAL   0
+#define ASUS_FAN_MODE_OVERBOOST1
+#define ASUS_FAN_MODE_OVERBOOST_MASK   0x01
+#define ASUS_FAN_MODE_SILENT   2
+#define ASUS_FAN_MODE_SILENT_MASK  0x02
+#define ASUS_FAN_MODES_MASK0x03
+
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
@@ -187,6 +195,10 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
 
+   bool fan_mode_available;
+   u8 fan_mode_mask;
+   u8 fan_mode;
+
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
struct mutex wmi_lock;
@@ -1483,6 +1495,116 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
 }
 
+/* Fan mode 
***/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+   u32 result;
+   int err;
+
+   asus->fan_mode_available = false;
+
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, );
+   if (err) {
+   if (err == -ENODEV)
+   return 0;
+   else
+   return err;
+   }
+
+   if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
+   (result & ASUS_FAN_MODES_MASK)) {
+   asus->fan_mode_available = true;
+   asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK;
+   }
+
+   return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+   int err;
+   u8 value;
+   u32 retval;
+
+   value = asus->fan_mode;
+
+   pr_info("Set fan mode: %u\n", value);
+   err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, );
+
+   if (err) {
+   pr_warn("Failed to set fan mode: %d\n", err);
+   return err;
+   }
+
+   if (retval != 1) {
+   pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
+   return -EIO;
+   }
+
+   return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+   if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) {
+   if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)
+   asus->fan_mode = ASUS_FAN_MODE_OVERBOOST;
+   else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
+   asus->fan_mode = ASUS_FAN_MODE_SILENT;
+   } else if (asus->fan_mode == ASUS_F

[PATCH v4 11/13] platform/x86: asus-wmi: Enhance detection of thermal data

2019-05-14 Thread Yurii Pavlovskyi
The obviously wrong value 1 for temperature device ID in this driver is
returned by at least some devices, including TUF Gaming series laptops,
instead of 0 as expected previously. Observable effect is that a
temp1_input in hwmon reads temperature near absolute zero.

Consider 0.1 K an erroneous value in addition to 0 K.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 7 +--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index bd9eb00f3a27..ffb4e2530ea4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1428,8 +1428,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct 
kobject *kobj,
else
ok = fan_attr <= asus->asus_hwmon_num_fans;
} else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
-   /* If value is zero, something is clearly wrong */
-   if (!value)
+   /*
+* If the temperature value in deci-Kelvin is near the absolute
+* zero temperature, something is clearly wrong
+*/
+   if (value == 0 || value == 1)
ok = false;
} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
ok = true;
-- 
2.17.1



[PATCH v4 10/13] platform/x86: asus-wmi: Organize code into sections

2019-05-14 Thread Yurii Pavlovskyi
The driver has grown pretty big and will grow more, which makes it hard to
navigate and understand. Add uniform comments to the code and ensure that
it is sorted into logical sections.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 89 +
 1 file changed, 47 insertions(+), 42 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 090a00af4017..bd9eb00f3a27 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -200,6 +200,8 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
 };
 
+/* Input 
**/
+
 static int asus_wmi_input_init(struct asus_wmi *asus)
 {
int err;
@@ -237,6 +239,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
+/* WMI 
/
+
 static int asus_wmi_evaluate_method3(u32 method_id,
u32 arg0, u32 arg1, u32 arg2, u32 *retval)
 {
@@ -349,9 +353,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi 
*asus, u32 dev_id)
  ASUS_WMI_DSTS_STATUS_BIT);
 }
 
-/*
- * LEDs
- */
+/* LEDs 
***/
+
 /*
  * These functions actually update the LED's, and are called from a
  * workqueue. By doing this as separate work rather than when the LED
@@ -661,6 +664,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
return rv;
 }
 
+/* RF 
*/
 
 /*
  * PCI hotplug (for wlan rfkill)
@@ -1083,6 +1087,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
return result;
 }
 
+/* Quirks 
*/
+
 static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
 {
struct pci_dev *xhci_pdev;
@@ -1115,9 +1121,8 @@ static void asus_wmi_set_als(void)
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
 }
 
-/*
- * Hwmon device
- */
+/* Hwmon device 
***/
+
 static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
  int *speed)
 {
@@ -1456,9 +1461,27 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
return 0;
 }
 
-/*
- * Backlight
- */
+static int asus_wmi_fan_init(struct asus_wmi *asus)
+{
+   int status;
+
+   asus->asus_hwmon_pwm = -1;
+   asus->asus_hwmon_num_fans = -1;
+   asus->asus_hwmon_fan_manual_mode = false;
+
+   status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans);
+   if (status) {
+   asus->asus_hwmon_num_fans = 0;
+   pr_warn("Could not determine number of fans: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+   return 0;
+}
+
+/* Backlight 
**/
+
 static int read_backlight_power(struct asus_wmi *asus)
 {
int ret;
@@ -1640,6 +1663,8 @@ static int is_display_toggle(int code)
return 0;
 }
 
+/* Fn-lock 
/
+
 static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
 {
u32 result;
@@ -1657,6 +1682,8 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
 }
 
+/* WMI events 
*/
+
 static int asus_wmi_get_event_code(u32 value)
 {
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -1790,9 +1817,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi 
*asus)
return -EIO;
 }
 
-/*
- * Sys helpers
- */
+/* Sysfs 
**/
+
 static int parse_arg(const char *buf, unsigned long count, int *val)
 {
if (!count)
@@ -1931,9 +1957,8 @@ static int asus_wmi_sysfs_init(struct platform_device 
*device)
return sysfs_create_group(>dev.kobj, _attribute_group);
 }
 
-/*
- * Platform device
- */
+/* Platform device 
/
+
 static int asus_wmi_platform_init(struct asus_wmi *asus)
 {
struct device *dev = >platform_device->dev;
@@ -2017,9 +2042,8 @@ static void asus_wmi_platform_exit(struct asus_wmi *asus)
asus_wmi_sysfs_exit(asus->platform_device);
 }
 
-/*
- * debugfs
- */
+/* debugfs 
/
+
 struct asus_wmi_debugfs_node {
struct asus_wmi *asus;
char *name;
@@ -2166,28 +2190,8 @@ static int asus_wmi_debugfs_init(struct asus_wmi *asus)
 

[PATCH v4 09/13] platform/x86: asus-wmi: Refactor error handling

2019-05-14 Thread Yurii Pavlovskyi
Remove exit label as it is only used once from the point in code where no
cleanup is required and return can be called immediately.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 6 +-
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 7bfac06abae4..090a00af4017 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -255,7 +255,7 @@ static int asus_wmi_evaluate_method3(u32 method_id,
 , );
 
if (ACPI_FAILURE(status))
-   goto exit;
+   return -EIO;
 
obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
@@ -266,10 +266,6 @@ static int asus_wmi_evaluate_method3(u32 method_id,
 
kfree(obj);
 
-exit:
-   if (ACPI_FAILURE(status))
-   return -EIO;
-
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV;
 
-- 
2.17.1



[PATCH v4 08/13] platform/x86: asus-nb-wmi: Add microphone mute key code

2019-05-14 Thread Yurii Pavlovskyi
The microphone mute key is missing from sparse keymap. It is present on
FX505GM and possibly other laptops. Add the missing code.

Also, comment on the fan mode switch key, which has the same code as the
already used key.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index b6f2ff95c3ed..d2399ce0b3cd 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -468,6 +468,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, },  /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
+   { KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
@@ -482,7 +483,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI 
*/
{ KE_KEY, 0x95, { KEY_MEDIA } },
-   { KE_KEY, 0x99, { KEY_PHONE } },
+   { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
-- 
2.17.1



[PATCH v4 07/13] platform/x86: asus-wmi: Support WMI event queue

2019-05-14 Thread Yurii Pavlovskyi
Event codes are expected to be retrieved from a queue on at least some
models. Specifically, very likely the ACPI WMI devices with _UID ATK are
queued whereas those with ASUSWMI are not [1].

The WMI event codes are pushed into a circular buffer queue. After the INIT
method is called, ACPI code is allowed to push events into this buffer.
The INIT method cannot be reverted. If the module is unloaded and an event
(such as hotkey press) gets emitted before inserting it back the events get
processed delayed by one or if the queue overflows, additionally delayed by
about 3 seconds.

It might be considered a minor issue and no normal user would likely
observe this (there is little reason unloading the driver), but it does
significantly frustrate a developer who is unlucky enough to encounter
this. Therefore, the fallback to unqueued behavior occurs whenever
something unexpected happens.

The fix flushes the old key codes out of the queue on load. After receiving
event the queue is read until either .. or 1 is encountered. Also as
noted in [1] it is checked whether notify code is equal to 0xFF before
enabling queue processing in WMI notify handler.

DSDT examples:

FX505GM
Device (ATKD)
{ ..
Name (ATKQ, Package (0x10)
{
0x, ..
}

Method (IANQ, 1, Serialized)
{
If ((AQNO >= 0x10))
{
Local0 = 0x64
While ((Local0 && (AQNO >= 0x10)))
{
Local0--
Sleep (0x0A)
}
...
..
AQTI++
AQTI &= 0x0F
ATKQ [AQTI] = Arg0
...
}

Method (GANQ, 0, Serialized)
{
..
If (AQNO)
{
...
Local0 = DerefOf (ATKQ [AQHI])
AQHI++
AQHI &= 0x0F
Return (Local0)
}

Return (One)
}

This code is almost identical to K54C, which does return Ones on empty
queue.

K54C:
Method (GANQ, 0, Serialized)
{
If (AQNO)
{
...
Return (Local0)
}

Return (Ones)
}

[1] Link: https://lkml.org/lkml/2019/4/12/104

Signed-off-by: Yurii Pavlovskyi 
Suggested-by: Daniel Drake 
---
 drivers/platform/x86/asus-wmi.c | 73 ++---
 1 file changed, 68 insertions(+), 5 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ed7c7857012e..7bfac06abae4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -84,6 +84,13 @@ MODULE_LICENSE("GPL");
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
 #define ASUS_ACPI_UID_ASUSWMI  "ASUSWMI"
+#define ASUS_ACPI_UID_ATK  "ATK"
+
+#define WMI_EVENT_QUEUE_SIZE   0x10
+#define WMI_EVENT_QUEUE_END0x1
+#define WMI_EVENT_MASK 0x
+/* The WMI hotkey event value is always the same. */
+#define WMI_EVENT_VALUE_ATK0xFF
 
 #define WMI_EVENT_MASK 0x
 
@@ -150,6 +157,7 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
+   bool wmi_event_queue;
 
struct input_dev *inputdev;
struct backlight_device *backlight_device;
@@ -1738,14 +1746,52 @@ static void asus_wmi_handle_event_code(int code, struct 
asus_wmi *asus)
 static void asus_wmi_notify(u32 value, void *context)
 {
struct asus_wmi *asus = context;
-   int code = asus_wmi_get_event_code(value);
+   int code;
+   int i;
 
-   if (code < 0) {
-   pr_warn("Failed to get notify code: %d\n", code);
-   return;
+   for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
+   code = asus_wmi_get_event_code(value);
+
+   if (code < 0) {
+   pr_warn("Failed to get notify code: %d\n", code);
+   return;
+   }
+
+   if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
+   return;
+
+   asus_wmi_handle_event_code(code, asus);
+
+   /*
+* Double check that queue is present:
+* ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
+*/
+   if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
+   return;
}
 
-   asus_wmi_handle_event_code(code, asus);
+   pr_warn("Failed to process event queue, last code: 0x%x\n", code);
+}
+
+static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
+{
+   int code;
+   int i;
+
+   for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
+   code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
+
+   if (code < 0) {
+   pr_warn("Failed to get event during flush: %d\n", code);
+   return code;
+   }
+
+   if (code == WMI_EVENT_QUEUE_END |

[PATCH v4 06/13] platform/x86: asus-wmi: Refactor WMI event handling

2019-05-14 Thread Yurii Pavlovskyi
Refactor WMI event handling into separate functions for getting the event
code and handling the retrieved event code as a preparation for
introduction of WMI event queue support.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 66 +
 1 file changed, 42 insertions(+), 24 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 5bdb4ffdbee3..ed7c7857012e 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -85,6 +85,8 @@ MODULE_LICENSE("GPL");
 
 #define ASUS_ACPI_UID_ASUSWMI  "ASUSWMI"
 
+#define WMI_EVENT_MASK 0x
+
 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
 
 static bool ashs_present(void)
@@ -1651,83 +1653,99 @@ static void asus_wmi_fnlock_update(struct asus_wmi 
*asus)
asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
 }
 
-static void asus_wmi_notify(u32 value, void *context)
+static int asus_wmi_get_event_code(u32 value)
 {
-   struct asus_wmi *asus = context;
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int code;
-   int orig_code;
-   unsigned int key_value = 1;
-   bool autorelease = 1;
 
status = wmi_get_event_data(value, );
-   if (status != AE_OK) {
-   pr_err("bad event status 0x%x\n", status);
-   return;
+   if (ACPI_FAILURE(status)) {
+   pr_warn("Failed to get WMI notify code: %s\n",
+   acpi_format_exception(status));
+   return -EIO;
}
 
obj = (union acpi_object *)response.pointer;
 
-   if (!obj || obj->type != ACPI_TYPE_INTEGER)
-   goto exit;
+   if (obj && obj->type == ACPI_TYPE_INTEGER)
+   code = (int)(obj->integer.value & WMI_EVENT_MASK);
+   else
+   code = -EIO;
+
+   kfree(obj);
+   return code;
+}
+
+static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
+{
+   int orig_code;
+   unsigned int key_value = 1;
+   bool autorelease = 1;
 
-   code = obj->integer.value;
orig_code = code;
 
if (asus->driver->key_filter) {
asus->driver->key_filter(asus->driver, , _value,
 );
if (code == ASUS_WMI_KEY_IGNORE)
-   goto exit;
+   return;
}
 
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
code = ASUS_WMI_BRN_UP;
-   else if (code >= NOTIFY_BRNDOWN_MIN &&
-code <= NOTIFY_BRNDOWN_MAX)
+   else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
code = ASUS_WMI_BRN_DOWN;
 
if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
asus_wmi_backlight_notify(asus, orig_code);
-   goto exit;
+   return;
}
}
 
if (code == NOTIFY_KBD_BRTUP) {
kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
-   goto exit;
+   return;
}
if (code == NOTIFY_KBD_BRTDWN) {
kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
-   goto exit;
+   return;
}
if (code == NOTIFY_KBD_BRTTOGGLE) {
if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
kbd_led_set_by_kbd(asus, 0);
else
kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
-   goto exit;
+   return;
}
 
if (code == NOTIFY_FNLOCK_TOGGLE) {
asus->fnlock_locked = !asus->fnlock_locked;
asus_wmi_fnlock_update(asus);
-   goto exit;
+   return;
}
 
-   if (is_display_toggle(code) &&
-   asus->driver->quirks->no_display_toggle)
-   goto exit;
+   if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
+   return;
 
if (!sparse_keymap_report_event(asus->inputdev, code,
key_value, autorelease))
pr_info("Unknown key %x pressed\n", code);
+}
 
-exit:
-   kfree(obj);
+static void asus_wmi_notify(u32 value, void *context)
+{
+   struct asus_wmi *asus = context;
+   int code = asus_wmi_get_event_code(value);
+
+   if (code < 0) {
+   pr_warn("Failed to get notify code: %d\n", code);
+   return;
+   }
+
+   asus_wmi_handle_event_code(code, asus);
 }
 
 /*
-- 
2.17.1



[PATCH v4 05/13] platform/x86: asus-wmi: Improve DSTS WMI method ID detection

2019-05-14 Thread Yurii Pavlovskyi
The DSTS method detection mistakenly selects DCTS instead of DSTS if
nothing is returned when the method ID is not defined in WMNB. As a result,
the control of keyboard backlight is not functional for TUF Gaming series
laptops. Implement detection based on _UID of the WMI device instead.

There is evidence that DCTS is handled by ACPI WMI devices that have _UID
ASUSWMI, whereas none of the devices without ASUSWMI respond to DCTS and
DSTS is used instead [1].

DSDT examples:

FX505GM (_UID ATK):
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (Zero)
}
...
// No return
}

K54C (_UID ATK):
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (0x02)
}
...
Return (0xFFFE)
}

[1] Link: https://lkml.org/lkml/2019/4/11/322

Signed-off-by: Yurii Pavlovskyi 
Suggested-by: Daniel Drake 

---
Depends on the previous patch in the series that adds method to
wmi module.
---
 drivers/hid/hid-asus.c |  2 +-
 drivers/platform/x86/asus-wmi.c| 23 +++---
 include/linux/platform_data/x86/asus-wmi.h |  4 ++--
 3 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 336aeaed1159..1d01fe23ca0c 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -396,7 +396,7 @@ static bool asus_kbd_wmi_led_control_present(struct 
hid_device *hdev)
if (!IS_ENABLED(CONFIG_ASUS_WMI))
return false;
 
-   ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2,
+   ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
   ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, );
hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
if (ret)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 6b35c1f00a3e..5bdb4ffdbee3 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -83,6 +83,8 @@ MODULE_LICENSE("GPL");
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
+#define ASUS_ACPI_UID_ASUSWMI  "ASUSWMI"
+
 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
 
 static bool ashs_present(void)
@@ -1874,6 +1876,8 @@ static int asus_wmi_sysfs_init(struct platform_device 
*device)
  */
 static int asus_wmi_platform_init(struct asus_wmi *asus)
 {
+   struct device *dev = >platform_device->dev;
+   char *wmi_uid;
int rv;
 
/* INIT enable hotkeys on some models */
@@ -1903,11 +1907,24 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
 * Note, on most Eeepc, there is no way to check if a method exist
 * or note, while on notebooks, they returns 0xFFFE on failure,
 * but once again, SPEC may probably be used for that kind of things.
+*
+* Additionally at least TUF Gaming series laptops return nothing for
+* unknown methods, so the detection in this way is not possible.
+*
+* There is strong indication that only ACPI WMI devices that have _UID
+* equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
 */
-   if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
+   wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
+   if (!wmi_uid)
+   return -ENODEV;
+
+   if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
+   dev_info(dev, "Detected ASUSWMI, use DCTS\n");
+   asus->dsts_id = ASUS_WMI_METHODID_DCTS;
+   } else {
+   dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
-   else
-   asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+   }
 
/* CWAP allow to define the behavior of the Fn+F2 key,
 * this method doesn't seems to be present on Eee PCs */
diff --git a/include/linux/platform_data/x86/asus-wmi.h 
b/include/linux/platform_data/x86/asus-wmi.h
index bfba245636a7..0668f76df921 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -18,8 +18,8 @@
 #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */
 #define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */
 #define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */
-#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */
-#define ASUS_WMI_METHODID_DSTS20x53545344 /* Device STatuS #2*/
+#define ASUS_WMI_METHODID_DCTS 0x53544344 /* Device status (DCTS) */
+#define ASUS_WMI_METHODID_DSTS 0x53545344 /* Device status (DSTS) */
 #define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */
 #define ASUS_WMI_ME

[PATCH v4 04/13] platform/x86: wmi: Add function to get _UID of WMI device

2019-05-14 Thread Yurii Pavlovskyi
Add a new function to acpi.h / wmi.c that returns _UID of the ACPI WMI
device. For example, it returns "ATK" for the following declaration in
DSDT:
Device (ATKD)
{
Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */)
  // _HID: Hardware ID
Name (_UID, "ATK")  // _UID: Unique ID
..

Generally, it is possible that multiple PNP0C14 ACPI devices are present in
the system as mentioned in the commit message of commit bff431e49ff5
("ACPI: WMI: Add ACPI-WMI mapping driver").

Therefore the _UID is returned for a specific ACPI device that declares the
given GUID, to which it is also mapped by other methods of wmi module.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/wmi.c | 19 +++
 include/linux/acpi.h   |  1 +
 2 files changed, 20 insertions(+)

diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 7b26b6ccf1a0..b08ffb769cbe 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -635,6 +635,25 @@ bool wmi_has_guid(const char *guid_string)
 }
 EXPORT_SYMBOL_GPL(wmi_has_guid);
 
+/**
+ * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID
+ * @guid_string: 36 char string of the form 
fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ *
+ * Find the _UID of ACPI device associated with this WMI GUID.
+ *
+ * Return: The ACPI _UID field value or NULL if the WMI GUID was not found
+ */
+char *wmi_get_acpi_device_uid(const char *guid_string)
+{
+   struct wmi_block *wblock = NULL;
+
+   if (!find_guid(guid_string, ))
+   return NULL;
+
+   return acpi_device_uid(wblock->acpi_device);
+}
+EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid);
+
 static struct wmi_block *dev_to_wblock(struct device *dev)
 {
return container_of(dev, struct wmi_block, dev.dev);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index d5dcebd7aad3..d31c7fd66f97 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -376,6 +376,7 @@ extern acpi_status wmi_install_notify_handler(const char 
*guid,
 extern acpi_status wmi_remove_notify_handler(const char *guid);
 extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out);
 extern bool wmi_has_guid(const char *guid);
+extern char *wmi_get_acpi_device_uid(const char *guid);
 
 #endif /* CONFIG_ACPI_WMI */
 
-- 
2.17.1



[PATCH v4 03/13] platform/x86: asus-wmi: Increase input buffer size of WMI methods

2019-05-14 Thread Yurii Pavlovskyi
The asus-nb-wmi driver is matched by WMI alias but fails to load on TUF
Gaming series laptops producing multiple ACPI errors in the kernel log.

The input buffer for WMI method invocation size is 2 dwords, whereas
3 are expected by this model.

FX505GM:
..
Method (WMNB, 3, Serialized)
{
P8XH (Zero, 0x11)
CreateDWordField (Arg2, Zero, IIA0)
CreateDWordField (Arg2, 0x04, IIA1)
CreateDWordField (Arg2, 0x08, IIA2)
Local0 = (Arg1 & 0x)
...

Compare with older K54C:
...
Method (WMNB, 3, NotSerialized)
{
CreateDWordField (Arg2, 0x00, IIA0)
CreateDWordField (Arg2, 0x04, IIA1)
Local0 = (Arg1 & 0x)
...

Increase buffer size to 3 dwords. No negative consequences of this change
are expected, as the input buffer size is not verified. The original
function is replaced by a wrapper for a new method passing value 0 for the
last parameter. The new function will be used to control RGB keyboard
backlight.

Signed-off-by: Yurii Pavlovskyi 
---
One of current kernel errors:
ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Field [IIA2] at bit offset/
length 64/32 exceeds size of target Buffer (64 bits)
(20190215/dsopcode-203)
[ 4528.573948] No Local Variables are initialized for Method [WMNB]
[ 4528.573949] Initialized Arguments for Method [WMNB]:  (3 arguments
defined for method invocation)
[ 4528.573950]   Arg0:   bd1bea5a 
Integer 
[ 4528.573952]   Arg1:   d414dc53 
Integer 4E464741
[ 4528.573954]   Arg2:   fcefea4b 
Buffer(8) F0 95 08 00 00 00 00 00
[ 4528.573959] ACPI Error: Aborting method \_SB.ATKD.WMNB due to previous
error (AE_AML_BUFFER_LIMIT) (20190215/psparse-531)
[ 4528.686425] asus-nb-wmi: probe of asus-nb-wmi failed with error -5
---
 drivers/platform/x86/asus-wmi.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 84d7fc6f941c..6b35c1f00a3e 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -98,6 +98,7 @@ static bool ashs_present(void)
 struct bios_args {
u32 arg0;
u32 arg1;
+   u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
 } __packed;
 
 /*
@@ -224,11 +225,13 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
-int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+static int asus_wmi_evaluate_method3(u32 method_id,
+   u32 arg0, u32 arg1, u32 arg2, u32 *retval)
 {
struct bios_args args = {
.arg0 = arg0,
.arg1 = arg1,
+   .arg2 = arg2,
};
struct acpi_buffer input = { (acpi_size) sizeof(args),  };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -260,6 +263,11 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 
arg1, u32 *retval)
 
return 0;
 }
+
+int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+{
+   return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
+}
 EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
 
 static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
-- 
2.17.1



[PATCH v4 02/13] platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load

2019-05-14 Thread Yurii Pavlovskyi
The error code and return value are mixed up. The intensity is always set
to 0 on load as kbd_led_read returns either 0 or negative value. To
reproduce set backlight to maximum, reload driver and try to increase it
using keyboard hotkey, the intensity will drop as a result. Correct the
implementation.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 62567766bdfb..84d7fc6f941c 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -595,8 +595,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
 
-   led_val = kbd_led_read(asus, NULL, NULL);
-   if (led_val >= 0) {
+   if (!kbd_led_read(asus, _val, NULL)) {
asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
-- 
2.17.1



[PATCH v4 01/13] platform/x86: asus-wmi: Fix hwmon device cleanup

2019-05-14 Thread Yurii Pavlovskyi
The driver does not clean up the hwmon device on exit or error. To
reproduce the bug, repeat rmmod, insmod to verify that device number
/sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Replace
call for registering device with devm_* version that unregisters it
automatically.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 7 ---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f94691615881..62567766bdfb 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1428,11 +1428,12 @@ __ATTRIBUTE_GROUPS(hwmon_attribute);
 
 static int asus_wmi_hwmon_init(struct asus_wmi *asus)
 {
+   struct device *dev = >platform_device->dev;
struct device *hwmon;
 
-   hwmon = hwmon_device_register_with_groups(>platform_device->dev,
- "asus", asus,
- hwmon_attribute_groups);
+   hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus,
+   hwmon_attribute_groups);
+
if (IS_ERR(hwmon)) {
pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon);
-- 
2.17.1



[PATCH v4 00/13] Support of ASUS TUF Gaming series laptops

2019-05-14 Thread Yurii Pavlovskyi
Hi,

this is the fourth version of the patch series. 

Changelog:
v4:
  * Rebase on for-next branch
  * Extract local variable in patch 01
  * Rename new method to "..._method3" and keep comma in struct declaration
in patch 03 (NOTE: the arg2 does not fit on same line by 1 character)
  * Patch "Improve DSTS WMI method ID detection":
- sort local variables
- use dev_info
- separate changes to wmi module in an own patch
- rename method ID constants and fix comment capitalization
  * "Support WMI event queue": split into separate refactoring and new
functionality patches, use dev_info as well
  * "Organize code into sections": split out error handling refactoring
  * "Enhance detection of thermal data": remove unreasonable refactoring
and just change the currently used condition
  * "Control RGB keyboard backlight": removed, will be posted afterwards.
I will follow on the status of the multicolor framework, it does look
promising for this.
  * Mark URL references with "Link:"
  * Minor corrections to commit messages
v3: 
  * Use devm_* function in patch 01
  * Detect DSTS/DCTS using _UID in patch 04
  * Detect event queue by _UID as well in patch 05
  * Rename poll function in patch 05
  * Fix terminology in patches 09 and 10
  * Correct commit messages
v2:
  * Fix logging

INTRODUCTION
The support for this laptop series is currently non-existent, as the
asus-nb-wmi driver (which is essentially configuration for asus-wmi) fails
to load and multiple ACPI errors are logged in dmesg. This patch series
adds pretty comprehensive support for these relatively new laptops, adds
some code organization, and fixes a couple of bugs in the asus-wmi module.

Thread for V1/V2: https://lkml.org/lkml/2019/4/10/973
Thread for V3: https://lkml.org/lkml/2019/4/19/178

Yurii Pavlovskyi (13):
  platform/x86: asus-wmi: Fix hwmon device cleanup
  platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on
load
  platform/x86: asus-wmi: Increase input buffer size of WMI methods
  platform/x86: wmi: Add function to get _UID of WMI device
  platform/x86: asus-wmi: Improve DSTS WMI method ID detection
  platform/x86: asus-wmi: Refactor WMI event handling
  platform/x86: asus-wmi: Support WMI event queue
  platform/x86: asus-nb-wmi: Add microphone mute key code
  platform/x86: asus-wmi: Refactor error handling
  platform/x86: asus-wmi: Organize code into sections
  platform/x86: asus-wmi: Enhance detection of thermal data
  platform/x86: asus-wmi: Switch fan boost mode
  platform/x86: asus-wmi: Do not disable keyboard backlight on unloading

 .../ABI/testing/sysfs-platform-asus-wmi   |  10 +
 drivers/hid/hid-asus.c|   2 +-
 drivers/platform/x86/asus-nb-wmi.c|   3 +-
 drivers/platform/x86/asus-wmi.c   | 427 ++
 drivers/platform/x86/wmi.c|  19 +
 include/linux/acpi.h  |   1 +
 include/linux/platform_data/x86/asus-wmi.h|   5 +-
 7 files changed, 374 insertions(+), 93 deletions(-)

-- 
2.17.1



Re: [PATCH v3 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight

2019-05-09 Thread Yurii Pavlovskyi
First of all, thanks to Andy for all the review comments!

I will implement all the ones that I didn't directly answer on as well and
update this series shortly.

Regarding this patch,

On 08.05.19 19:12, Pavel Machek wrote:
>> Shouldn't be the LED subsystem driver for this?
> 
> Yes, please. We have common interface for LED drivers; this needs to
> use it.

That is indeed a better option and I did in fact considered this first and
even did a test implementation. The discoveries were:
1. The WMI methods are write-only and only written all at once in a
transaction manner (also invoking solely first RGB-interface method has no
effect until some other keyboard backlight method is called).
2. In addition to RGB there are several control values, which switch
effects, speed and enable or disable the backlight under specific
conditions or switch whether it is set temporarily or permanently (not that
these are critical functionalities, but for the sake of completeness).
3. The EC is really slow
# time bash -c "echo 1 > /sys/devices/platform/faustus/kbbl_set"

real0m0,691s
user0m0,000s
sys 0m0,691s

(please ignore the sysfs-path there, it's essentially the same code running
as in this patch). It is consistently same for both temporary and permanent
configuration. Writing after every change would take about (6+)x of that.
Not that it's that unbearable though as it is not likely to be done often.

I was not quite happy with that implementation so I opted for writing sort
of sysfs wrapper instead that would allow same sort of transactions as
provided by BIOS. I agree that it's non-standard solution.

If I understood correctly, the typical current RGB led_class devices from
the Linux tree currently provide channels as separate LEDs. There are also
blink / pattern options present, I guess one could misuse them for setting
effects and speed. So one could make 3 devices for RGB + 3 for awake,
sleep, boot modes + 1 for setting effect / speed.

I'd guess the end solution might be also either something like combination
of both approaches (RGB leds + separate sysfs interface) or some extension
of the led_class device interface. Dropping support of the non-essential
features for the sake of uniformity of ABI would also be an option to
consider (exposing just three RGB LEDs with brightness only), not happy one
though.

In any case this looks like it might need some additional research,
discussion, development, and a pair of iterations so I tend to separate
this patch from the series and post it extra after the others are through
to avoid dragging 10+ patches around.

Any suggestions on how to do this properly would be appreciated. That's the
best I could come up with at the moment.

Thanks,
Yurii


Re: [PATCH v3 08/11] platform/x86: asus-wmi: Enhance detection of thermal data

2019-05-09 Thread Yurii Pavlovskyi
On 08.05.19 15:58, Andy Shevchenko wrote:
> On Fri, Apr 19, 2019 at 1:12 PM Yurii Pavlovskyi
>  wrote:
> 
>> -   if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 
>> 0xFFF8
>> +   if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 
>> 0xFFF8)
> 
> Seems like a bug fix and thus should be a separate commit predecessing
> the series.
The previous one should theoretically work as well, just thought that would
help readability, will revert this.

>> -   else if (attr == _attr_temp1_input.attr)
>> -   dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
>
> I don't see how this change affects the user output or driver
> behaviour. Why is it done?
> 
>> -   } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
> 
>> +   } else if (attr == _attr_temp1_input.attr) {
> 
> So, I don't see why you change this line.
> 
Yes, looking at this patch now I'd guess the refactoring there is really
misguided as it adds a lot more code than it removes, will drop it
completely and just add a new condition to the current check instead in
next version:
-   /* If value is zero, something is clearly wrong */
-   if (!value)
+   if (!value || value == 1)

Thanks,
Yurii


Re: [PATCH v3 05/11] platform/x86: asus-wmi: Support WMI event queue

2019-05-09 Thread Yurii Pavlovskyi
On 08.05.19 15:47, Andy Shevchenko wrote:
> On Fri, Apr 19, 2019 at 1:10 PM Yurii Pavlovskyi
>  wrote:
> 
> It's rather a big change. Can it be split to smaller pieces?
> 
I will then split this into refactoring event code handling in separate
methods and actual event queue support patches.


Re: [PATCH v3 04/11] platform/x86: asus-wmi: Improve DSTS WMI method ID detection

2019-05-09 Thread Yurii Pavlovskyi
On 08.05.19 15:36, Andy Shevchenko wrote:> On Fri, Apr 19, 2019 at 1:08 PM
Yurii Pavlovskyi
>  wrote:
>> int rv;
>> +   char *wmi_uid;
>
> Keep them in reversed spruce order.

What do you mean by that? Do you mean like this?
+ char *wmi_uid;
int rv;
int err;

>> +#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS
(DCTS) */
>
> It's not clear from the description what 'C' stands for.
> Is there any specification which describes the difference and actual
> abbreviations?
>
Not that I know of. Daniel had written some research in the previous
version thread regarding where it is used, but as I understand from current
implementation the functional difference is not really there, as it serves
the same purpose as DSTS, just for another hardware.

On 09.05.19 08:08, Daniel Drake wrote:
> For clarity I think the constants could be renamed as
> ASUS_WMI_METHODID_DCTS and ASUS_WMI_METHODID_DSTS.
>
Agree, that'll be better.

Thanks,
Yurii


Re: [PATCH v3 08/11] platform/x86: asus-wmi: Enhance detection of thermal data

2019-04-25 Thread Yurii Pavlovskyi
On 24.04.19 20:25, Pawnikar, Sumeet R wrote:
>> +/*
>> + * If the temperature value in deci-Kelvin is near the absolute
>> + * zero temperature, something is clearly wrong.
>> + */
>> +if (!value || value == 1)
>> +return 0;
> Do you still need to return 0 in case of wrong/failure case ? 
> Shouldn't you return error here ? 

Yes, I think 0 is right there, it's not an error condition in the check
itself (such as IO or memory error). The function returns two values: check
result in asus->asus_hwmon_thermal_available and error code.

This is still a successful negative result. The 0 or 1 (as added in this
patch) there merely indicates that the temperature measurement is not
available this way. It is safe to assume that no device that does provide
temperature will return 0.1 K. The temperature measurement is not critical
for driver operation so the driver proceeds without it.

One could return ENODEV when the check result is negative, but that would
just move the actual check and assignment further to asus_wmi_fan_init.

This is how it might look like in DSDT in this case:
Method (TMPR, 0, NotSerialized)
{
Return (One)
}
..
If ((IIA0 == 0x00110011))
{
Return ((TMPR () & 0x))
}

This is indeed the right method ID but it's nothing there.

Regards,
Yurii


[PATCH v3 10/11] platform/x86: asus-wmi: Switch fan boost mode

2019-04-19 Thread Yurii Pavlovskyi
The WMI exposes a write-only device ID where up to three fan modes modes
can be switched on some laptops (TUF Gaming FX505GM). There is a hotkey
combination Fn-F5 that does have a fan icon which is designed to toggle
between fan modes. The DSTS of the device ID returns information about the
presence of the device and the presence of each of the two additional fan
modes as a bitmask (0x01 - overboost present, 0x02 - silent present) [1].

Add a SysFS entry that reads the last written value and updates value in
WMI on write and a hotkey handler that toggles the modes taking into
account their availability according to DSTS.

Modes:
* 0x00 - normal or balanced,
* 0x01 - overboost, increased fan RPM,
* 0x02 - silent, decreased fan RPM

[1] https://lkml.org/lkml/2019/4/12/110

Signed-off-by: Yurii Pavlovskyi 
Suggested-by: Daniel Drake 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  10 ++
 drivers/platform/x86/asus-wmi.c   | 149 +-
 include/linux/platform_data/x86/asus-wmi.h|   1 +
 3 files changed, 152 insertions(+), 8 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 1cc54d5e3e10..6f396c4eabdc 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -97,3 +97,13 @@ Description:
Write changed RGB keyboard backlight parameters:
* 1 - permanently,
* 2 - temporarily.
+
+What:  /sys/devices/platform//fan_mode
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   Fan boost mode:
+   * 0 - normal,
+   * 1 - overboost,
+   * 2 - silent
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 0a32079336d8..7974283b7b12 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -69,6 +69,7 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_KBD_BRTUP   0xc4
 #define NOTIFY_KBD_BRTDWN  0xc5
 #define NOTIFY_KBD_BRTTOGGLE   0xc7
+#define NOTIFY_KBD_FBM 0x99
 
 #define ASUS_FAN_DESC  "cpu_fan"
 #define ASUS_FAN_MFUN  0x13
@@ -77,6 +78,13 @@ MODULE_LICENSE("GPL");
 #define ASUS_FAN_CTRL_MANUAL   1
 #define ASUS_FAN_CTRL_AUTO 2
 
+#define ASUS_FAN_MODE_NORMAL   0
+#define ASUS_FAN_MODE_OVERBOOST1
+#define ASUS_FAN_MODE_OVERBOOST_MASK   0x01
+#define ASUS_FAN_MODE_SILENT   2
+#define ASUS_FAN_MODE_SILENT_MASK  0x02
+#define ASUS_FAN_MODES_MASK0x03
+
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
@@ -198,6 +206,10 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
 
+   bool fan_mode_available;
+   u8 fan_mode_mask;
+   u8 fan_mode;
+
bool kbbl_rgb_available;
struct asus_kbbl_rgb kbbl_rgb;
 
@@ -1827,6 +1839,114 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
 }
 
+/* Fan mode 
***/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+   u32 result;
+   int err;
+
+   asus->fan_mode_available = false;
+
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, );
+   if (err) {
+   if (err == -ENODEV)
+   return 0;
+   else
+   return err;
+   }
+
+   if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
+   (result & ASUS_FAN_MODES_MASK)) {
+   asus->fan_mode_available = true;
+   asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK;
+   }
+
+   return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+   int err;
+   u8 value;
+   u32 retval;
+
+   value = asus->fan_mode;
+
+   pr_info("Set fan mode: %u\n", value);
+   err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, );
+
+   if (err) {
+   pr_warn("Failed to set fan mode: %d\n", err);
+   return err;
+   }
+
+   if (retval != 1) {
+   pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
+   return -EIO;
+   }
+
+   return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+   if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) {
+   if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)
+   asus->fan_mode = ASUS_FAN_MODE_OVERBOOST;
+   else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
+   asus->fan_mode = ASUS_FAN_MODE_SILENT;
+ 

[PATCH v3 08/11] platform/x86: asus-wmi: Enhance detection of thermal data

2019-04-19 Thread Yurii Pavlovskyi
The obviously wrong value 1 for temperature device ID in this driver is
returned by at least some devices, including TUF Gaming series laptops,
instead of 0 as expected previously. Observable effect is that a
temp1_input in hwmon reads temperature near absolute zero.

* Consider 0.1 K an erroneous value in addition to 0 K.
* Refactor detection of thermal input availability to a separate function.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 45 -
 1 file changed, 38 insertions(+), 7 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index e69e55635afb..1b8272374660 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -178,6 +178,7 @@ struct asus_wmi {
struct asus_rfkill gps;
struct asus_rfkill uwb;
 
+   bool asus_hwmon_thermal_available;
bool asus_hwmon_fan_manual_mode;
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
@@ -1375,6 +1376,32 @@ static struct attribute *hwmon_attributes[] = {
NULL
 };
 
+static int asus_hwmon_check_thermal_available(struct asus_wmi *asus)
+{
+   u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
+   int err;
+
+   asus->asus_hwmon_thermal_available = false;
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, );
+
+   if (err < 0) {
+   if (err == -ENODEV)
+   return 0;
+
+   return err;
+   }
+
+   /*
+* If the temperature value in deci-Kelvin is near the absolute
+* zero temperature, something is clearly wrong.
+*/
+   if (!value || value == 1)
+   return 0;
+
+   asus->asus_hwmon_thermal_available = true;
+   return 0;
+}
+
 static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
  struct attribute *attr, int idx)
 {
@@ -1388,8 +1415,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject 
*kobj,
 
if (attr == _attr_pwm1.attr)
dev_id = ASUS_WMI_DEVID_FAN_CTRL;
-   else if (attr == _attr_temp1_input.attr)
-   dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
if (attr == _attr_fan1_input.attr
|| attr == _attr_fan1_label.attr
@@ -1414,15 +1439,13 @@ static umode_t asus_hwmon_sysfs_is_visible(struct 
kobject *kobj,
 * - reverved bits are non-zero
 * - sfun and presence bit are not set
 */
-   if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF8
+   if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 0xFFF8)
|| (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
ok = false;
else
ok = fan_attr <= asus->asus_hwmon_num_fans;
-   } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
-   /* If value is zero, something is clearly wrong */
-   if (!value)
-   ok = false;
+   } else if (attr == _attr_temp1_input.attr) {
+   ok = asus->asus_hwmon_thermal_available;
} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
ok = true;
} else {
@@ -1469,6 +1492,14 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
}
 
pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+
+   status = asus_hwmon_check_thermal_available(asus);
+   if (status) {
+   pr_warn("Could not check if thermal available: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info("Thermal available: %d\n", asus->asus_hwmon_thermal_available);
return 0;
 }
 
-- 
2.17.1



[PATCH v3 04/11] platform/x86: asus-wmi: Improve DSTS WMI method ID detection

2019-04-19 Thread Yurii Pavlovskyi
The DSTS method detection mistakenly selects DCTS instead of DSTS if
nothing is returned when the method ID is not defined in WMNB. As a result,
the control of keyboard backlight is not functional for TUF Gaming series
laptops. Implement another detection method instead.

There is evidence that DCTS is handled by ACPI WMI devices that have _UID
ASUSWMI, whereas none of the devices without ASUSWMI respond to DCTS and
DSTS is used instead [1]. To check the _UID a new method is added to wmi.h
/ wmi.c. It returns _UID of the ACPI WMI device that declares WMI object
with given GUID.

Generally, it is possible that multiple PNP0C14 ACPI devices are present in
the system as mentioned in the commit message of commit bff431e49ff5
("ACPI: WMI: Add ACPI-WMI mapping driver").

Therefore the _UID is checked for given GUID that maps to a specific ACPI
device, to which it is also mapped by other methods of wmi module.

DSDT examples:

FX505GM:
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (Zero)
}
...
// No return
}

K54C:
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (0x02)
}
...
Return (0xFFFE)
}

[1] https://lkml.org/lkml/2019/4/11/322

Signed-off-by: Yurii Pavlovskyi 
Suggested-by: Daniel Drake 
---
 drivers/platform/x86/asus-wmi.c| 20 ++--
 drivers/platform/x86/wmi.c | 19 +++
 include/linux/acpi.h   |  1 +
 include/linux/platform_data/x86/asus-wmi.h |  4 ++--
 4 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ba04737ece0d..266d0eda5476 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -80,6 +80,8 @@ MODULE_LICENSE("GPL");
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
+#define ASUS_ACPI_UID_ASUSWMI  "ASUSWMI"
+
 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
 
 static bool ashs_present(void)
@@ -1847,6 +1849,7 @@ static int asus_wmi_sysfs_init(struct platform_device 
*device)
 static int asus_wmi_platform_init(struct asus_wmi *asus)
 {
int rv;
+   char *wmi_uid;
 
/* INIT enable hotkeys on some models */
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, ))
@@ -1875,11 +1878,24 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
 * Note, on most Eeepc, there is no way to check if a method exist
 * or note, while on notebooks, they returns 0xFFFE on failure,
 * but once again, SPEC may probably be used for that kind of things.
+*
+* Additionally at least TUF Gaming series laptops return nothing for
+* unknown methods, so the detection in this way is not possible.
+*
+* There is strong indication that only ACPI WMI devices that have _UID
+* equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
 */
-   if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
+   wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
+   if (!wmi_uid)
+   return -ENODEV;
+
+   if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
+   pr_info("Detected ASUSWMI, use DCTS\n");
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
-   else
+   } else {
+   pr_info("Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+   }
 
/* CWAP allow to define the behavior of the Fn+F2 key,
 * this method doesn't seems to be present on Eee PCs */
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 7b26b6ccf1a0..b08ffb769cbe 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -635,6 +635,25 @@ bool wmi_has_guid(const char *guid_string)
 }
 EXPORT_SYMBOL_GPL(wmi_has_guid);
 
+/**
+ * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID
+ * @guid_string: 36 char string of the form 
fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ *
+ * Find the _UID of ACPI device associated with this WMI GUID.
+ *
+ * Return: The ACPI _UID field value or NULL if the WMI GUID was not found
+ */
+char *wmi_get_acpi_device_uid(const char *guid_string)
+{
+   struct wmi_block *wblock = NULL;
+
+   if (!find_guid(guid_string, ))
+   return NULL;
+
+   return acpi_device_uid(wblock->acpi_device);
+}
+EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid);
+
 static struct wmi_block *dev_to_wblock(struct device *dev)
 {
return container_of(dev, struct wmi_block, dev.dev);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index d5dcebd7aad3..d31c7fd66f97 100644
--- a/include/linux/acpi.h
+++ b/include/linux/

[PATCH v3 06/11] platform/x86: asus-nb-wmi: Add microphone mute key code

2019-04-19 Thread Yurii Pavlovskyi
The microphone mute key is missing from sparse keymap. It is present on
FX505GM and possibly other laptops. Add the missing code.

Also, comment on the fan mode switch key, which has the same code as the
already used key.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index b6f2ff95c3ed..d2399ce0b3cd 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -468,6 +468,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, },  /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
+   { KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
@@ -482,7 +483,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI 
*/
{ KE_KEY, 0x95, { KEY_MEDIA } },
-   { KE_KEY, 0x99, { KEY_PHONE } },
+   { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
-- 
2.17.1



[PATCH v3 02/11] platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load

2019-04-19 Thread Yurii Pavlovskyi
The error code and return value are mixed up. The intensity is always set
to 0 on load as kbd_led_read returns either 0 or negative value. To
reproduce set backlight to maximum, reload driver and try to increase it
using keyboard hotkey, the intensity will drop as a result. Correct the
implementation.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index d865eb95054c..731ffd382426 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -590,8 +590,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
 
-   led_val = kbd_led_read(asus, NULL, NULL);
-   if (led_val >= 0) {
+   if (!kbd_led_read(asus, _val, NULL)) {
asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
-- 
2.17.1



[PATCH v3 01/11] platform/x86: asus-wmi: Fix hwmon device cleanup

2019-04-19 Thread Yurii Pavlovskyi
The driver does not clean up the hwmon device on exit or error. To
reproduce the bug, repeat rmmod, insmod to verify that device number
/sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Replace
call for registering device with devm_* version that unregisters it
automatically.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 7 ---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ee1fa93708ec..d865eb95054c 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1425,9 +1425,10 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
 {
struct device *hwmon;
 
-   hwmon = hwmon_device_register_with_groups(>platform_device->dev,
- "asus", asus,
- hwmon_attribute_groups);
+   hwmon = devm_hwmon_device_register_with_groups(
+   >platform_device->dev, "asus", asus,
+   hwmon_attribute_groups);
+
if (IS_ERR(hwmon)) {
pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon);
-- 
2.17.1



[PATCH v3 11/11] platform/x86: asus-wmi: Do not disable keyboard backlight on unloading

2019-04-19 Thread Yurii Pavlovskyi
The keyboard backlight is automatically disabled when the module is
unloaded as it is exposed as a ledclass device. Change this behavior to
ignore setting brightness when the device is in unloading state.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 7974283b7b12..02c9639bf54f 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -483,6 +483,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, 
int value)
 static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
 {
+   /* Prevent disabling keyboard backlight on module unregister */
+   if (led_cdev->flags & LED_UNREGISTERING)
+   return;
+
do_kbd_led_set(led_cdev, value);
 }
 
-- 
2.17.1



[PATCH v3 03/11] platform/x86: asus-wmi: Increase the input buffer size of WMI methods

2019-04-19 Thread Yurii Pavlovskyi
The asus-nb-wmi driver is matched by WMI alias but fails to load on TUF
Gaming series laptops producing multiple ACPI errors in the kernel log.

The input buffer for WMI method invocation size is 2 dwords, whereas
3 are expected by this model.

FX505GM:
..
Method (WMNB, 3, Serialized)
{
P8XH (Zero, 0x11)
CreateDWordField (Arg2, Zero, IIA0)
CreateDWordField (Arg2, 0x04, IIA1)
CreateDWordField (Arg2, 0x08, IIA2)
Local0 = (Arg1 & 0x)
...

Compare with older K54C:
...
Method (WMNB, 3, NotSerialized)
{
CreateDWordField (Arg2, 0x00, IIA0)
CreateDWordField (Arg2, 0x04, IIA1)
Local0 = (Arg1 & 0x)
...

Increase buffer size to 3 dwords. No negative consequences of this change
are expected, as the input buffer size is not verified. The original
function is replaced by a wrapper for a new method passing value 0 for the
last parameter. The new function will be used to control RGB keyboard
backlight.

Signed-off-by: Yurii Pavlovskyi 
---
One of current kernel errors:
ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Field [IIA2] at bit offset/
length 64/32 exceeds size of target Buffer (64 bits)
(20190215/dsopcode-203)
[ 4528.573948] No Local Variables are initialized for Method [WMNB]
[ 4528.573949] Initialized Arguments for Method [WMNB]:  (3 arguments
defined for method invocation)
[ 4528.573950]   Arg0:   bd1bea5a 
Integer 
[ 4528.573952]   Arg1:   d414dc53 
Integer 4E464741
[ 4528.573954]   Arg2:   fcefea4b 
Buffer(8) F0 95 08 00 00 00 00 00
[ 4528.573959] ACPI Error: Aborting method \_SB.ATKD.WMNB due to previous
error (AE_AML_BUFFER_LIMIT) (20190215/psparse-531)
[ 4528.686425] asus-nb-wmi: probe of asus-nb-wmi failed with error -5
---
 drivers/platform/x86/asus-wmi.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 731ffd382426..ba04737ece0d 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -95,6 +95,7 @@ static bool ashs_present(void)
 struct bios_args {
u32 arg0;
u32 arg1;
+   u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
 } __packed;
 
 /*
@@ -219,11 +220,13 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
-int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1,
+   u32 arg2, u32 *retval)
 {
struct bios_args args = {
.arg0 = arg0,
.arg1 = arg1,
+   .arg2 = arg2
};
struct acpi_buffer input = { (acpi_size) sizeof(args),  };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -255,6 +258,11 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 
arg1, u32 *retval)
 
return 0;
 }
+
+int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+{
+   return asus_wmi_evaluate_method_3dw(method_id, arg0, arg1, 0, retval);
+}
 EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
 
 static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
-- 
2.17.1



[PATCH v3 07/11] platform/x86: asus-wmi: Organize code into sections

2019-04-19 Thread Yurii Pavlovskyi
The driver has grown pretty big and will grow more, which makes it hard to
navigate and understand. Add uniform comments to the code and ensure that
it is sorted into logical sections.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 94 -
 1 file changed, 46 insertions(+), 48 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f782dac4cbe7..e69e55635afb 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -193,6 +193,8 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
 };
 
+/* Input 
**/
+
 static int asus_wmi_input_init(struct asus_wmi *asus)
 {
int err;
@@ -230,6 +232,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
+/* WMI 
/
+
 static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1,
u32 arg2, u32 *retval)
 {
@@ -248,7 +252,7 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 
arg0, u32 arg1,
 , );
 
if (ACPI_FAILURE(status))
-   goto exit;
+   return -EIO;
 
obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
@@ -259,10 +263,6 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 
arg0, u32 arg1,
 
kfree(obj);
 
-exit:
-   if (ACPI_FAILURE(status))
-   return -EIO;
-
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV;
 
@@ -346,9 +346,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi 
*asus, u32 dev_id)
  ASUS_WMI_DSTS_STATUS_BIT);
 }
 
-/*
- * LEDs
- */
+/* LEDs 
***/
+
 /*
  * These functions actually update the LED's, and are called from a
  * workqueue. By doing this as separate work rather than when the LED
@@ -658,6 +657,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
return rv;
 }
 
+/* RF 
*/
 
 /*
  * PCI hotplug (for wlan rfkill)
@@ -1080,6 +1080,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
return result;
 }
 
+/* Quirks 
*/
+
 static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
 {
struct pci_dev *xhci_pdev;
@@ -1112,9 +1114,8 @@ static void asus_wmi_set_als(void)
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
 }
 
-/*
- * Hwmon device
- */
+/* Hwmon device 
***/
+
 static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
  int *speed)
 {
@@ -1390,7 +1391,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject 
*kobj,
else if (attr == _attr_temp1_input.attr)
dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
-
if (attr == _attr_fan1_input.attr
|| attr == _attr_fan1_label.attr
|| attr == _attr_pwm1.attr
@@ -1453,9 +1453,27 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
return 0;
 }
 
-/*
- * Backlight
- */
+static int asus_wmi_fan_init(struct asus_wmi *asus)
+{
+   int status;
+
+   asus->asus_hwmon_pwm = -1;
+   asus->asus_hwmon_num_fans = -1;
+   asus->asus_hwmon_fan_manual_mode = false;
+
+   status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans);
+   if (status) {
+   asus->asus_hwmon_num_fans = 0;
+   pr_warn("Could not determine number of fans: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+   return 0;
+}
+
+/* Backlight 
**/
+
 static int read_backlight_power(struct asus_wmi *asus)
 {
int ret;
@@ -1637,6 +1655,8 @@ static int is_display_toggle(int code)
return 0;
 }
 
+/* WMI events 
*/
+
 static int asus_wmi_get_next_event(u32 value)
 {
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -1762,9 +1782,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi 
*asus)
return -EIO;
 }
 
-/*
- * Sys helpers
- */
+/* Sysfs 
**/
+
 static int parse_arg(const char *buf, unsigned long count, int *val)
 {
if (!count)
@@ -1903,9 +1922,8 @@ static int asus_wmi_sysfs_init(struct platform_device 
*device)
return sysfs_create_group(>dev.kobj, _attribute_group)

[PATCH v3 05/11] platform/x86: asus-wmi: Support WMI event queue

2019-04-19 Thread Yurii Pavlovskyi
Event codes are expected to be retrieved from a queue on at least some
models. Specifically, very likely the ACPI WMI devices with _UID ATK are
queued whereas those with ASUSWMI are not [1].

The WMI event codes are pushed into a circular buffer queue. After the INIT
method is called, ACPI code is allowed to push events into this buffer.
The INIT method cannot be reverted. If the module is unloaded and an event
(such as hotkey press) gets emitted before inserting it back the events get
processed delayed by one or if the queue overflows, additionally delayed by
about 3 seconds.

It might be considered a minor issue and no normal user would likely
observe this (there is little reason unloading the driver), but it does
significantly frustrate a developer who is unlucky enough to encounter
this. Therefore, the fallback to unqueued behavior occurs whenever
something unexpected happens.

The fix flushes the old key codes out of the queue on load. After receiving
event the queue is read until either .. or 1 is encountered. Also as
noted in [1] it is checked whether notify code is equal to 0xFF before
enabling queue processing in WMI notify handler.

DSDT examples:

FX505GM
Device (ATKD)
{ ..
Name (ATKQ, Package (0x10)
{
0x, ..
}

Method (IANQ, 1, Serialized)
{
If ((AQNO >= 0x10))
{
Local0 = 0x64
While ((Local0 && (AQNO >= 0x10)))
{
Local0--
Sleep (0x0A)
}
...
..
AQTI++
AQTI &= 0x0F
ATKQ [AQTI] = Arg0
...
}

Method (GANQ, 0, Serialized)
{
..
If (AQNO)
{
...
Local0 = DerefOf (ATKQ [AQHI])
AQHI++
AQHI &= 0x0F
Return (Local0)
}

Return (One)
}

This code is almost identical to K54C, which does return Ones on empty
queue.

K54C:
Method (GANQ, 0, Serialized)
{
If (AQNO)
{
...
Return (Local0)
}

Return (Ones)
}

[1] https://lkml.org/lkml/2019/4/12/104

Signed-off-by: Yurii Pavlovskyi 
Suggested-by: Daniel Drake 
---
 drivers/platform/x86/asus-wmi.c | 136 +---
 1 file changed, 108 insertions(+), 28 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 266d0eda5476..f782dac4cbe7 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -81,6 +81,13 @@ MODULE_LICENSE("GPL");
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
 #define ASUS_ACPI_UID_ASUSWMI  "ASUSWMI"
+#define ASUS_ACPI_UID_ATK  "ATK"
+
+#define WMI_EVENT_QUEUE_SIZE   0x10
+#define WMI_EVENT_QUEUE_END0x1
+#define WMI_EVENT_MASK 0x
+/* The WMI hotkey event value is always the same. */
+#define WMI_EVENT_VALUE_ATK0xFF
 
 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
 
@@ -145,6 +152,7 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
+   bool wmi_event_queue;
 
struct input_dev *inputdev;
struct backlight_device *backlight_device;
@@ -1629,77 +1637,129 @@ static int is_display_toggle(int code)
return 0;
 }
 
-static void asus_wmi_notify(u32 value, void *context)
+static int asus_wmi_get_next_event(u32 value)
 {
-   struct asus_wmi *asus = context;
-   struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
-   int code;
-   int orig_code;
-   unsigned int key_value = 1;
-   bool autorelease = 1;
+   int code = -EIO;
 
-   status = wmi_get_event_data(value, );
-   if (status != AE_OK) {
-   pr_err("bad event status 0x%x\n", status);
-   return;
+   status = wmi_get_event_data(value, );
+   if (ACPI_FAILURE(status)) {
+   pr_warn("Failed to get WMI event code: %s\n",
+   acpi_format_exception(status));
+   return code;
}
 
-   obj = (union acpi_object *)response.pointer;
+   obj = (union acpi_object *)output.pointer;
 
-   if (!obj || obj->type != ACPI_TYPE_INTEGER)
-   goto exit;
+   if (obj && obj->type == ACPI_TYPE_INTEGER)
+   code = (int)(obj->integer.value & WMI_EVENT_MASK);
+
+   kfree(obj);
+   return code;
+}
+
+static void asus_wmi_handle_notify_code(int code, struct asus_wmi *asus)
+{
+   int orig_code;
+   unsigned int key_value = 1;
+   bool autorelease = 1;
 
-   code = obj->integer.value;
orig_code = code;
 
if (asus->driver->key_filter) {
asus->driver->key_filter(asus->driver, 

[PATCH v3 00/11] asus-wmi: Support of ASUS TUF Gaming series laptops

2019-04-19 Thread Yurii Pavlovskyi
Hi,

this is the third version of the patch series. 

Changelog:
v3: 
  * Use devm_* function in patch 01
  * Detect DSTS/DCTS using _UID in patch 04
  * Detect event queue by _UID as well in patch 05
  * Rename poll function in patch 05
  * Fix terminology in patches 09 and 10
  * Correct commit messages
v2:
  * Fix logging

INTRODUCTION
The support for this laptop series is currently non-existent, as the
asus-nb-wmi driver (which is essentially configuration for asus-wmi) fails
to load and multiple ACPI errors are logged in dmesg. This patch series
adds pretty comprehensive support for these relatively new laptops, adds
some code organization, and fixes a couple of bugs in the asus-wmi module.

Original message from V1/V2:
https://lkml.org/lkml/2019/4/10/973

It is really long, so I will not copy it completely here, please refer
to the original for notes on design decisions and existing minor issues
(other than quirks, which should be hopefully solved now).

Yurii Pavlovskyi (11):
  platform/x86: asus-wmi: Fix hwmon device cleanup
  platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on
load
  platform/x86: asus-wmi: Increase the input buffer size of WMI methods
  platform/x86: asus-wmi: Improve DSTS WMI method ID detection
  platform/x86: asus-wmi: Support WMI event queue
  platform/x86: asus-nb-wmi: Add microphone mute key code
  platform/x86: asus-wmi: Organize code into sections
  platform/x86: asus-wmi: Enhance detection of thermal data
  platform/x86: asus-wmi: Control RGB keyboard backlight
  platform/x86: asus-wmi: Switch fan boost mode
  platform/x86: asus-wmi: Do not disable keyboard backlight on unloading

 .../ABI/testing/sysfs-platform-asus-wmi   |  71 ++
 drivers/platform/x86/asus-nb-wmi.c|   3 +-
 drivers/platform/x86/asus-wmi.c   | 797 +++---
 drivers/platform/x86/wmi.c|  19 +
 include/linux/acpi.h  |   1 +
 include/linux/platform_data/x86/asus-wmi.h|   7 +-
 6 files changed, 797 insertions(+), 101 deletions(-)

-- 
2.17.1



[PATCH v3 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight

2019-04-19 Thread Yurii Pavlovskyi
The WMI exposes two methods for controlling RGB keyboard backlight, which
allows controlling:
* RGB components in range 00 - ff,
* Switch between 4 effects,
* Switch between 3 effect speed modes,
* Separately enable the backlight on boot, in the awake state (after driver
  load), in sleep mode, and probably in something called shutdown mode (no
  observable effects of enabling it are known so far).

The configuration should be written to several sysfs parameter buffers
which are then written via WMI by writing either 1 or 2 to the "kbbl_set"
parameter. When reading the buffers the last written value is returned.

If the 2 is written to "kbbl_set", the parameters will be reset on reboot
(temporary mode), 1 is permanent mode, parameters are retained.

The calls use new 3-dword input buffer method call.

The functionality is only enabled if corresponding DSTS methods return
exact valid values.

The following script demonstrates usage:

echo Red [00 - ff]
echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red
echo Green [00 - ff]
echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green
echo Blue [00 - ff]
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue
echo Mode: 0 - static color, 1 - breathing, 2 - color cycle, 3 - strobing
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode
echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed
echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep,
echo 2a or ff to set all
echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags
echo Save: 1 - permanently, 2 - temporarily, reset after reboot
echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set

Signed-off-by: Yurii Pavlovskyi 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  61 
 drivers/platform/x86/asus-wmi.c   | 331 ++
 include/linux/platform_data/x86/asus-wmi.h|   2 +
 3 files changed, 394 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..1cc54d5e3e10 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,64 @@ KernelVersion:   3.5
 Contact:   "AceLan Kao" 
 Description:
Resume on lid open. 1 means on, 0 means off.
+
+What:  /sys/devices/platform//kbbl/kbbl_red
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight red component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_green
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight green component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_blue
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight blue component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_mode
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight mode:
+   * 0 - static color,
+   * 1 - breathing,
+   * 2 - color cycle,
+   * 3 - strobing.
+
+What:  /sys/devices/platform//kbbl/kbbl_speed
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight speed for modes 1 and 2:
+   * 0 - slow,
+   * 1 - medium,
+   * 2 - fast.
+
+What:  /sys/devices/platform//kbbl/kbbl_flags
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight enable flags (2a to enable everything), 
OR of:
+   * 02 - on boot (until module load),
+   * 08 - awake,
+   * 20 - sleep.
+
+What:      /sys/devices/platform//kbbl/kbbl_set
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   Write changed RGB keyboard backlight parameters:
+   * 1 - permanently,
+   * 2 - temporarily.
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 1b8272374660..0a32079336d8 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -148,6 +148,21 @@ struct asus_rfkill {
u32 dev_id;
 };
 
+struct asus_kbbl_rgb {
+   u8 kbbl_red;
+   u8 kbbl_green;
+   u8 kbbl_blue;
+   u8 kbbl_mode;
+   u8 kbbl_speed;
+
+   u8 kbbl_set_red;
+   u8 kbbl_set_green;
+   u8 kbbl_set_b

Re: [PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode

2019-04-12 Thread Yurii Pavlovskyi
On 12.04.19 10:03, Daniel Drake wrote:
> On Thu, Apr 11, 2019 at 1:47 PM Yurii Pavlovskyi
>  wrote:
>> * 0x00 - is normal,
>> * 0x01 - is obviously turbo by the amount of noise, might be useful to
>> avoid CPU frequency throttling on high load,
>> * 0x02 - the meaning is unknown at the time as modes are not named
>> in the vendor documentation, but it does look like a quiet mode as CPU
>> temperature does increase about 10 degrees on maximum load.
> 
> I'm curious which vendor documentation you're working with here.

That would be user manual for FX505 series, which is pretty minimalistic.
It says it has a fan mode switch key and that's it. Following up on your
comment, I've searched more and actually did found their names hidden in
marketing web page on the website. You're right, they call them balanced,
overboost and silent there.

> From the spec,
> 0 = normal
> 1 = overboost
> 2 = silent

> Also you can use DSTS on the 0x00110018 device to check the exact
> capabilities supported, which you should use to refine which modes can
> be cycled through.
> Bit 0 = overboost supported
> Bit 1 = silent supported
> 
> Thanks
> Daniel
> 

Thanks! I was guessing that the 3 in DSTS must've meant something.

Appreciate your comments! Will definitely implement them. I'm going to post
the v3 patch series approximately middle of next week.

Best regards,
Yurii


Re: [PATCH v2 05/11] platform/x86: asus-wmi: Support queued WMI event codes

2019-04-12 Thread Yurii Pavlovskyi



On 12.04.19 09:48, Daniel Drake wrote:
> On Thu, Apr 11, 2019 at 1:44 PM Yurii Pavlovskyi
>  wrote:
>> Event codes are expected to be polled from a queue on at least some
>> models.
> 
> Maybe avoid the word "poll" since it suggests something else (at least to me).

Ok, will change this in the code as well.

>>> The fix flushes the old key codes out of the queue on load and after
>> receiving event the queue is read until either .. or 1 is encountered.
>>
>> It might be considered a minor issue and no normal user would likely to
>> observe this (there is little reason unloading the driver), but it does
>> significantly frustrate a developer who is unlucky enough to encounter
>> this.
>>
>> Introduce functionality for flushing and processing queued codes, which is
>> enabled via quirk flag for ASUS7000. It might be considered if it is
>> reasonable to enable it everywhere (might introduce regressions) or always
>> try to flush the queue on module load and try to detect if this quirk is
>> present in the future.
>>
>> This patch limits the effect to the specific hardware defined by ASUS7000
>> device that is used for driver detection by vendor driver of Fx505. The
>> fallback is also implemented in case initial flush fails.
> 
> Which vendor driver are you referring to here?

The ASUS Keyboard hotkeys Driver V2.0.5 which is available to download for
FX505 has this in his INF file:
[ATKP.ntamd64]

%DeviceDesc1% = NO_DRV64, ACPI\ASUS7000

But this driver is not generic. It is limited to very specific hardware,
I'd guess gaming series (for instance K54C does not have this device). It
was rather a way to avoid breaking anything. I think your suggestion below
is much better.

> 
> Researching more:
> In our database of 144 Asus models, 142 of them have GANQ.
> 
> Of those 142, 9 of them return One in the empty-queue case. The other
> 133 match your FX505GM device exactly. So it seems valid to interpret
> both 0x and 0x1 as queue-end markers.
> 
> The 2 devices that do not have GANQ are not laptops. They also do not
> have the _UID "ATK" WMI device, they only have "ASUSWMI" and their WMI
> _WED method does not use a queue.
> There are a few more units that have both ASUSWMI and ATK devices, and
> the ASUSWMI device appears to never be queued.
> Another observation is that the ASUSWMI device works with notify code
> 0xD2, and the ATK device works with 0xFF.
> 
> Nailing this down a bit further, I found a DSDT for EEEPC X101H: that
> one only has ASUSWMI and it is also not queued.
> 
> So I think you should make this queue behaviour applied more
> generically, but either avoid it when the WMI device _UID is not "ATK"
> (as discussed in the DCTS/DSTS thread), or avoid it when the notify
> code is not 0x>
> Thanks
> Daniel

Thanks a lot for your research, it's much appreciated! That seems to
confirm that these two quirks are actually connected with ATK device. I
guess it makes sense to combine the detection for both of them. Also to
flush the queue we need to know the notify code beforehand, because it is
checked in _WED so checking for ATK seems reasonable to me.

Best regards,
Yurii


Re: [PATCH 04/11] platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection

2019-04-12 Thread Yurii Pavlovskyi
Wow that is a great thing you done, thanks a lot for your time and your
kind words! Your suggestion indeed sounds good judging by your results.

Both of my devices have UID "ATK" (actually FX505 also has two other
PNP0C14 devices with HIDs SampleDev and TestDev, but I think they are
disabled by default).

It looks like the driver is loaded already only if ASUS_WMI_MGMT_GUID is
present, so I guess that could be used for wmi_driver_register. A little
obstacle is that ACPI device pointer is stored in wmi_block structure,
which is internal to the wmi.c. This means we would have to add a new
method to wmi.h / wmi.c for getting the UID of WMI ACPI device.

I guess it might be possible to somehow navigate the device tree back to
the platform driver of WMI module, but it would definitely be more obscure,
and searching for the device by HID again is not only slower but generally
would require a guarantee that it's the same device. So adding a new
function looks reasonable to me. It might also be useful to someone
implementing similar things for other vendors in the future.

I'm going to implement this in updated patch.

Thanks,
Yurii

On 11.04.19 12:55, Daniel Drake wrote:
> On Thu, Apr 11, 2019 at 4:28 AM Yurii Pavlovskyi
>  wrote:
>> The DSTS method detection fails, as nothing is returned if method is not
>> defined in WMNB. As a result the control of keyboard backlight is not
>> functional for TUF Gaming series laptops (at the time the only
>> functionality of the driver on this model implemented with WMI methods).
>>
>> Patch was tested on a newer TUF Gaming FX505GM and older K54C model.
>>
>> FX505GM:
>> Method (WMNB, 3, Serialized)
>> { ...
>> If ((Local0 == 0x53545344))
>> {
>> ...
>> Return (Zero)
>> }
>> ...
>> // No return
>> }
>>
>> K54C:
>> Method (WMNB, 3, Serialized)
>> { ...
>> If ((Local0 == 0x53545344))
>> {
>> ...
>> Return (0x02)
>> }
>> ...
>> Return (0xFFFE)
>> }
>>
>> The non-existing method ASUS_WMI_METHODID_DSTS=0x53544344 (actually it is
>> DCTS in little endian ASCII) is selected in asus->dsts.
>>
>> One way to fix this would be to call both for every known device ID until
>> some answers - this would increase module load time.
>>
>> Another option is to check some device that is known to exist on every
>> model - none known at the time.
>>
>> Last option, which is implemented, is to check for presence of the
>> ASUS7000 device in ACPI tree (it is a dummy device), which is the
>> condition used for loading the vendor driver for this model. This might
>> not fix every affected model ever produced, but it likely does not
>> introduce any regressions. The patch introduces a quirk that is enabled
>> when ASUS7000 is found.
>>
>> Scope (_SB)
>> {
>> Device (ATK)
>> {
>> Name (_HID, "ASUS7000")  // _HID: Hardware ID
>> }
>> }
> 
> Hmm, tricky! But about time we did something about the DSTS vs DCTS guessing.
> 
> While we don't have definitive knowledge of the right thing to do
> here, I think I have enough info available to solidify some
> assumptions we'd otherwise be afraid to make, and then we can
> implement a better approach here.
> 
> In our database of 144 Asus DSDTs, 14 of them respond to DCTS:
> 
> AS_D520MT
> D425MC
> D640SA
> D320SF-K
> D415MT
> D830MT
> G11DF
> M32CD4-K
> V221ID
> V272UN_SKU3
> Z240IE
> ZN220IC-K
> ZN241IC
> ZN270IE
> 
> Of those 14, they all additionally respond to DSTS, except for D415MT
> and AS_D520MT.
> 
> In all 14 cases, the DCTS handling is done inside a device with _UID
> ASUSWMI. None of the other 130 products have a device with that _UID.
> 
> Furthermore, we recently received access to the ASUS spec, which
> confirms that the Instance Name for EeePC is "ACPI\PNP0C14\ASUSWMI_0"
> whereas the Instance Name for other notebooks is "ACPI\PNP0C14\ATK_0".
> 
> The 12 devices that respond to both DCTS and DSTS, do it on separate
> different devices, albeit with the same _WDG UUID. The one with _UID
> ASUSWMI responds to DCTS, and the one with _UID ATK responds to DSTS.
> 
> So that seems fairly convincing. My thinking is that we can check the
> _UID of the underlying device, and use DCTS for ASUSWMI, DSTS
> otherwise. To do that, I think you'd have to rework the driver probing
> so that it happens through wmi_driver_register(). Then from the probe
> function onwards you will get a wmi_device, and hopefully you can get
> the _UID through that instance somehow.
> 
> There's a separate issue of what happens on those 12 machines where
> there are two WMI devs, with the same _WDG GUID.
> drivers/platform/x86/wmi.c drops duplicates, so one of them is being
> ignored in that case. We'd ideally find a way to ignore the ASUSWMI
> node and go with ATK in that situation. But I think this can be
> separated from your work here.
> 
> Thanks for these patches and welcome to the world of kernel development!
> 
> Daniel
> 


Re: [PATCH 01/11] platform/x86: asus-wmi: Fix hwmon device cleanup

2019-04-12 Thread Yurii Pavlovskyi
Hello Daniel,

thank you for the tip. I did not know about this function. It does indeed
seem to make this complete patch redundant looking at
6e5f62b9e3651e61 hwmon: (lm90) Use devm_hwmon_device_register_with_groups

I will surely implement it this way in the next version.

Best regards,
Yurii

On 11.04.19 10:21, Daniel Drake wrote:
> On Thu, Apr 11, 2019 at 4:21 AM Yurii Pavlovskyi
>  wrote:
>>
>> The asus-wmi driver does not clean up the hwmon device on exit or error.
>> To reproduce the bug, repeat rmmod, insmod to verify that device number
>> /sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Add
>> pointer to the device in module state and call cleanup on error.
> 
> I wonder if this can be fixed more cleanly by using
> devm_hwmon_device_register_with_groups() ?
> 
> Thanks
> Daniel
> 


[PATCH v2 11/11] platform/x86: asus-wmi: Do not disable keyboard backlight on unload

2019-04-10 Thread Yurii Pavlovskyi
The keyboard backlight is disabled when module is unloaded as it is
exposed as LED device. Change this behavior to ignore setting 0 brightness
when the ledclass device is unloading.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f0e506feb924..f49992fa87b3 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -475,6 +475,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, 
int value)
 static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
 {
+   /* Prevent disabling keyboard backlight on module unregister */
+   if (led_cdev->flags & LED_UNREGISTERING)
+   return;
+
do_kbd_led_set(led_cdev, value);
 }
 
-- 
2.17.1



[PATCH v2 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight

2019-04-10 Thread Yurii Pavlovskyi
The WMI exposes two methods for controlling RGB keyboard backlight which
allow to control:
* RGB components in range 00 - ff,
* Switch between 4 effects,
* Switch between 3 effect speed modes,
* Separately enable the backlight on boot, in awake state (after driver
  load), in sleep mode, and probably in something called shutdown mode
  (no observable effects of enabling it are known so far).

The configuration should be written to several sysfs parameter buffers
which are then written via WMI by writing either 1 or 2 to the "kbbl_set"
parameter. When reading the buffers the last written value is returned.

If the 2 is written to "kbbl_set", the parameters will be reset on reboot
(temporary mode), 1 is permanent mode, parameters are retained.

The calls use new 3-dword input buffer method call.

The functionality is only enabled if corresponding DSTS methods return
exact valid values.

The following script demonstrates usage:

echo Red [00 - ff]
echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red
echo Green [00 - ff]
echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green
echo Blue [00 - ff]
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue
echo Mode: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode
echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed
echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep,
echo 2a or ff to set all
echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags
echo Save: 1 - permanently, 2 - temporarily, reset after reboot
echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set

Signed-off-by: Yurii Pavlovskyi 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  61 
 drivers/platform/x86/asus-wmi.c   | 329 ++
 include/linux/platform_data/x86/asus-wmi.h|   2 +
 3 files changed, 392 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..300a40519695 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,64 @@ KernelVersion:   3.5
 Contact:   "AceLan Kao" 
 Description:
Resume on lid open. 1 means on, 0 means off.
+
+What:  /sys/devices/platform//kbbl/kbbl_red
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight red component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_green
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight green component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_blue
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight blue component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_mode
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight mode:
+   * 0 - static color,
+   * 1 - blink,
+   * 2 - rainbow,
+   * 3 - strobe.
+
+What:  /sys/devices/platform//kbbl/kbbl_speed
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight speed for modes 1 and 2:
+   * 0 - slow,
+   * 1 - medium,
+   * 2 - fast.
+
+What:  /sys/devices/platform//kbbl/kbbl_flags
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight enable flags (2a to enable everything), 
OR of:
+   * 02 - on boot (until module load),
+   * 08 - awake,
+   * 20 - sleep.
+
+What:      /sys/devices/platform//kbbl/kbbl_set
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   Write changed RGB keyboard backlight parameters:
+   * 1 - permanently,
+   * 2 - temporarily.
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index de0a8f61d4a1..b4fd200e8335 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -145,6 +145,21 @@ struct asus_rfkill {
u32 dev_id;
 };
 
+struct asus_kbbl_rgb {
+   u8 kbbl_red;
+   u8 kbbl_green;
+   u8 kbbl_blue;
+   u8 kbbl_mode;
+   u8 kbbl_speed;
+
+   u8 kbbl_set_red;
+   u8 kbbl_set_green;
+   u8 kbbl_set_blue;
+   u8 kbbl_set_m

[PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode

2019-04-10 Thread Yurii Pavlovskyi
The WMI exposes a write-only device ID where three modes can be switched
on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5
that does have a fan icon which is designed to toggle between these 3
modes.

Add a SysFS entry that reads the last written value and updates value in
WMI on write and a hotkey handler that toggles the modes. The
corresponding DEVS device handler does obviously take 3 possible
argument values.

Method (SFBM, 1, NotSerialized)
{
If ((Arg0 == Zero) { .. }
If ((Arg0 == One)) { .. }
If ((Arg0 == 0x02)) { .. }
}

... // DEVS
If ((IIA0 == 0x00110018))
{
   SFBM (IIA1)
   Return (One)
}

* 0x00 - is normal,
* 0x01 - is obviously turbo by the amount of noise, might be useful to
avoid CPU frequency throttling on high load,
* 0x02 - the meaning is unknown at the time as modes are not named
in the vendor documentation, but it does look like a quiet mode as CPU
temperature does increase about 10 degrees on maximum load.

Signed-off-by: Yurii Pavlovskyi 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  10 ++
 drivers/platform/x86/asus-wmi.c   | 119 --
 include/linux/platform_data/x86/asus-wmi.h|   1 +
 3 files changed, 117 insertions(+), 13 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 300a40519695..2b3184e297a7 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -97,3 +97,13 @@ Description:
Write changed RGB keyboard backlight parameters:
* 1 - permanently,
* 2 - temporarily.
+
+What:  /sys/devices/platform//fan_mode
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   Fan boost mode:
+   * 0 - normal,
+   * 1 - turbo,
+   * 2 - quiet?
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index b4fd200e8335..f0e506feb924 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -69,6 +69,7 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_KBD_BRTUP   0xc4
 #define NOTIFY_KBD_BRTDWN  0xc5
 #define NOTIFY_KBD_BRTTOGGLE   0xc7
+#define NOTIFY_KBD_FBM 0x99
 
 #define ASUS_FAN_DESC  "cpu_fan"
 #define ASUS_FAN_MFUN  0x13
@@ -77,6 +78,8 @@ MODULE_LICENSE("GPL");
 #define ASUS_FAN_CTRL_MANUAL   1
 #define ASUS_FAN_CTRL_AUTO 2
 
+#define ASUS_FAN_MODE_COUNT3
+
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
@@ -196,6 +199,9 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
 
+   bool fan_mode_available;
+   u8 fan_mode;
+
bool kbbl_rgb_available;
struct asus_kbbl_rgb kbbl_rgb;
 
@@ -1832,6 +1838,87 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
 }
 
+/* Fan mode 
***/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+   u32 result;
+   int err;
+
+   asus->fan_mode_available = false;
+
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, );
+   if (err) {
+   if (err == -ENODEV)
+   return 0;
+   else
+   return err;
+   }
+
+   if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
+   asus->fan_mode_available = true;
+
+   return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+   int err;
+   u8 value;
+   u32 retval;
+
+   value = asus->fan_mode % ASUS_FAN_MODE_COUNT;
+   pr_info("Set fan mode: %u\n", value);
+   err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, );
+
+   if (err) {
+   pr_warn("Failed to set fan mode: %d\n", err);
+   return err;
+   }
+
+   if (retval != 1) {
+   pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
+   return -EIO;
+   }
+
+   return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+   asus->fan_mode = (asus->fan_mode + 1) % ASUS_FAN_MODE_COUNT;
+   return fan_mode_write(asus);
+}
+
+static ssize_t fan_mode_show(struct device *dev,
+   struct device_attribute *attr, char *buf)
+{
+   struct asus_wmi *asus = dev_get_drvdata(dev);
+
+   return show_u8(asus->fan_mode, buf);
+}
+
+static ssize_t fan_mode_store(struct device *dev,
+   struct device_attribute *attr, const char *buf, size_t count)
+{
+   int result;
+   u8 new_mode;
+
+   struct asus_wmi *asus = dev_get_drvdata(dev);
+
+   result = store_

[PATCH v2 08/11] platform/x86: asus-wmi: Enhance detection of thermal data

2019-04-10 Thread Yurii Pavlovskyi
The obviously wrong value 1 for temperature device ID in this driver is
returned by at least some devices, including TUF Gaming series laptops,
instead of 0 as expected previously. Observable effect is that a
temp1_input in hwmon reads temperature near absolute zero.

* Consider 0.1 K as erroneous value in addition to 0 K.
* Refactor detection of thermal input availability to a separate function.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 45 -
 1 file changed, 38 insertions(+), 7 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index a98df005d6cb..de0a8f61d4a1 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -176,6 +176,7 @@ struct asus_wmi {
struct asus_rfkill gps;
struct asus_rfkill uwb;
 
+   bool asus_hwmon_thermal_available;
bool asus_hwmon_fan_manual_mode;
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
@@ -1373,6 +1374,32 @@ static struct attribute *hwmon_attributes[] = {
NULL
 };
 
+static int asus_hwmon_check_thermal_available(struct asus_wmi *asus)
+{
+   u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
+   int err;
+
+   asus->asus_hwmon_thermal_available = false;
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, );
+
+   if (err < 0) {
+   if (err == -ENODEV)
+   return 0;
+
+   return err;
+   }
+
+   /*
+* If the temperature value in deci-Kelvin is near the absolute
+* zero temperature, something is clearly wrong.
+*/
+   if (!value || value == 1)
+   return 0;
+
+   asus->asus_hwmon_thermal_available = true;
+   return 0;
+}
+
 static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
  struct attribute *attr, int idx)
 {
@@ -1386,8 +1413,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject 
*kobj,
 
if (attr == _attr_pwm1.attr)
dev_id = ASUS_WMI_DEVID_FAN_CTRL;
-   else if (attr == _attr_temp1_input.attr)
-   dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
if (attr == _attr_fan1_input.attr
|| attr == _attr_fan1_label.attr
@@ -1412,15 +1437,13 @@ static umode_t asus_hwmon_sysfs_is_visible(struct 
kobject *kobj,
 * - reverved bits are non-zero
 * - sfun and presence bit are not set
 */
-   if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF8
+   if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 0xFFF8)
|| (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
ok = false;
else
ok = fan_attr <= asus->asus_hwmon_num_fans;
-   } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
-   /* If value is zero, something is clearly wrong */
-   if (!value)
-   ok = false;
+   } else if (attr == _attr_temp1_input.attr) {
+   ok = asus->asus_hwmon_thermal_available;
} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
ok = true;
} else {
@@ -1476,6 +1499,14 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
}
 
pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+
+   status = asus_hwmon_check_thermal_available(asus);
+   if (status) {
+   pr_warn("Could not check if thermal available: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info("Thermal available: %d\n", asus->asus_hwmon_thermal_available);
return 0;
 }
 
-- 
2.17.1



[PATCH v2 06/11] platform/x86: asus-nb-wmi: Add microphone mute key code

2019-04-10 Thread Yurii Pavlovskyi
The microphone mute key that is present on FX505GM laptop and possibly
others is missing from sparse keymap. Add the missing code.

Also comment on the fan mode switch key that has the same code as the
already used key.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index 357d273ed336..39cf447198a9 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -474,6 +474,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, },  /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
+   { KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
@@ -488,7 +489,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI 
*/
{ KE_KEY, 0x95, { KEY_MEDIA } },
-   { KE_KEY, 0x99, { KEY_PHONE } },
+   { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
-- 
2.17.1



[PATCH v2 07/11] platform/x86: asus-wmi: Organize code into sections

2019-04-10 Thread Yurii Pavlovskyi
The driver has grown (and will more) pretty big which makes it hard to
navigate and understand. Add uniform comments to the code and ensure that
it is sorted into logical sections.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 94 -
 1 file changed, 46 insertions(+), 48 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 5aa30f8a2a38..a98df005d6cb 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -191,6 +191,8 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
 };
 
+/* Input 
**/
+
 static int asus_wmi_input_init(struct asus_wmi *asus)
 {
int err;
@@ -228,6 +230,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
+/* WMI 
/
+
 static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1,
u32 arg2, u32 *retval)
 {
@@ -246,7 +250,7 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 
arg0, u32 arg1,
 , );
 
if (ACPI_FAILURE(status))
-   goto exit;
+   return -EIO;
 
obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
@@ -257,10 +261,6 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 
arg0, u32 arg1,
 
kfree(obj);
 
-exit:
-   if (ACPI_FAILURE(status))
-   return -EIO;
-
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV;
 
@@ -344,9 +344,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi 
*asus, u32 dev_id)
  ASUS_WMI_DSTS_STATUS_BIT);
 }
 
-/*
- * LEDs
- */
+/* LEDs 
***/
+
 /*
  * These functions actually update the LED's, and are called from a
  * workqueue. By doing this as separate work rather than when the LED
@@ -656,6 +655,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
return rv;
 }
 
+/* RF 
*/
 
 /*
  * PCI hotplug (for wlan rfkill)
@@ -1078,6 +1078,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
return result;
 }
 
+/* Quirks 
*/
+
 static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
 {
struct pci_dev *xhci_pdev;
@@ -1110,9 +1112,8 @@ static void asus_wmi_set_als(void)
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
 }
 
-/*
- * Hwmon device
- */
+/* Hwmon device 
***/
+
 static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
  int *speed)
 {
@@ -1388,7 +1389,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject 
*kobj,
else if (attr == _attr_temp1_input.attr)
dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
-
if (attr == _attr_fan1_input.attr
|| attr == _attr_fan1_label.attr
|| attr == _attr_pwm1.attr
@@ -1460,9 +1460,27 @@ static void asus_wmi_hwmon_exit(struct asus_wmi *asus)
}
 }
 
-/*
- * Backlight
- */
+static int asus_wmi_fan_init(struct asus_wmi *asus)
+{
+   int status;
+
+   asus->asus_hwmon_pwm = -1;
+   asus->asus_hwmon_num_fans = -1;
+   asus->asus_hwmon_fan_manual_mode = false;
+
+   status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans);
+   if (status) {
+   asus->asus_hwmon_num_fans = 0;
+   pr_warn("Could not determine number of fans: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+   return 0;
+}
+
+/* Backlight 
**/
+
 static int read_backlight_power(struct asus_wmi *asus)
 {
int ret;
@@ -1644,6 +1662,8 @@ static int is_display_toggle(int code)
return 0;
 }
 
+/* WMI events 
*/
+
 static int asus_poll_wmi_event(u32 value)
 {
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -1766,9 +1786,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi 
*asus)
return -EIO;
 }
 
-/*
- * Sys helpers
- */
+/* Sysfs 
**/
+
 static int parse_arg(const char *buf, unsigned long count, int *val)
 {
if (!count)
@@ -1907,9 +1926,8 @@ static int asus_wmi_sysfs_init(struct platform_device 
*device)
return sysfs_create_group(>dev.kobj, _attribute_group)

[PATCH v2 05/11] platform/x86: asus-wmi: Support queued WMI event codes

2019-04-10 Thread Yurii Pavlovskyi
Event codes are expected to be polled from a queue on at least some
models.

The WMI event codes are pushed into queue based on circular buffer. After
INIT method is called ACPI code is allowed to push events into this buffer
the INIT method can not be reverted. If the module is unloaded and an
event (such as hotkey press) gets emitted before inserting it back the
events get processed delayed by one or, if the queue overflows,
additionally delayed by about 3 seconds.

Patch was tested on a newer TUF Gaming FX505GM and older K54C model.

FX505GM
Device (ATKD)
{ ..
Name (ATKQ, Package (0x10)
{
0x, ..
}

Method (IANQ, 1, Serialized)
{
If ((AQNO >= 0x10))
{
Local0 = 0x64
While ((Local0 && (AQNO >= 0x10)))
{
Local0--
Sleep (0x0A)
}
...
..
AQTI++
AQTI &= 0x0F
ATKQ [AQTI] = Arg0
...
}

Method (GANQ, 0, Serialized)
{
..
If (AQNO)
{
...
Local0 = DerefOf (ATKQ [AQHI])
AQHI++
AQHI &= 0x0F
Return (Local0)
}

Return (One)
}

This code is almost identical to K54C, which does return Ones on empty
queue.

K54C:
Method (GANQ, 0, Serialized)
{
If (AQNO)
{
...
Return (Local0)
}

Return (Ones)
}

The fix flushes the old key codes out of the queue on load and after
receiving event the queue is read until either .. or 1 is encountered.

It might be considered a minor issue and no normal user would likely to
observe this (there is little reason unloading the driver), but it does
significantly frustrate a developer who is unlucky enough to encounter
this.

Introduce functionality for flushing and processing queued codes, which is
enabled via quirk flag for ASUS7000. It might be considered if it is
reasonable to enable it everywhere (might introduce regressions) or always
try to flush the queue on module load and try to detect if this quirk is
present in the future.

This patch limits the effect to the specific hardware defined by ASUS7000
device that is used for driver detection by vendor driver of Fx505. The
fallback is also implemented in case initial flush fails.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c |   1 +
 drivers/platform/x86/asus-wmi.c| 122 ++---
 drivers/platform/x86/asus-wmi.h|   2 +
 3 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index cc5f0765a8d9..357d273ed336 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -438,6 +438,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver 
*driver)
 
if (acpi_dev_found("ASUS7000")) {
driver->quirks->force_dsts = true;
+   driver->quirks->wmi_event_queue = true;
}
 }
 
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 80f3447734fc..5aa30f8a2a38 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -80,6 +80,12 @@ MODULE_LICENSE("GPL");
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
+#define WMI_EVENT_QUEUE_SIZE   0x10
+#define WMI_EVENT_QUEUE_END0x1
+#define WMI_EVENT_MASK 0x
+/* The event value is always the same. */
+#define WMI_EVENT_VALUE0xFF
+
 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
 
 static bool ashs_present(void)
@@ -143,6 +149,7 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
+   bool wmi_event_queue;
 
struct input_dev *inputdev;
struct backlight_device *backlight_device;
@@ -1637,77 +1644,126 @@ static int is_display_toggle(int code)
return 0;
 }
 
-static void asus_wmi_notify(u32 value, void *context)
+static int asus_poll_wmi_event(u32 value)
 {
-   struct asus_wmi *asus = context;
-   struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
-   int code;
-   int orig_code;
-   unsigned int key_value = 1;
-   bool autorelease = 1;
+   int code = -EIO;
 
-   status = wmi_get_event_data(value, );
-   if (status != AE_OK) {
-   pr_err("bad event status 0x%x\n", status);
-   return;
+   status = wmi_get_event_data(value, );
+   if (ACPI_FAILURE(status)) {
+   pr_warn("Failed to get WMI event code: %s\n",
+   acpi_format_exception(status));
+   return code;
}
 
-   

[PATCH v2 04/11] platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection

2019-04-10 Thread Yurii Pavlovskyi
The DSTS method detection fails, as nothing is returned if method is not
defined in WMNB. As a result the control of keyboard backlight is not
functional for TUF Gaming series laptops (at the time the only
functionality of the driver on this model implemented with WMI methods).

Patch was tested on a newer TUF Gaming FX505GM and older K54C model.

FX505GM:
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (Zero)
}
...
// No return
}

K54C:
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (0x02)
}
...
Return (0xFFFE)
}

The non-existing method ASUS_WMI_METHODID_DSTS=0x53544344 (actually it is
DCTS in little endian ASCII) is selected in asus->dsts.

One way to fix this would be to call both for every known device ID until
some answers - this would increase module load time.

Another option is to check some device that is known to exist on every
model - none known at the time.

Last option, which is implemented, is to check for presence of the
ASUS7000 device in ACPI tree (it is a dummy device), which is the
condition used for loading the vendor driver for this model. This might
not fix every affected model ever produced, but it likely does not
introduce any regressions. The patch introduces a quirk that is enabled
when ASUS7000 is found.

Scope (_SB)
{
Device (ATK)
{
Name (_HID, "ASUS7000")  // _HID: Hardware ID
}
}

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c |  5 +
 drivers/platform/x86/asus-wmi.c| 14 --
 drivers/platform/x86/asus-wmi.h|  5 +
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index b6f2ff95c3ed..cc5f0765a8d9 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -28,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "asus-wmi.h"
 
@@ -434,6 +435,10 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver 
*driver)
}
pr_info("Using i8042 filter function for receiving events\n");
}
+
+   if (acpi_dev_found("ASUS7000")) {
+   driver->quirks->force_dsts = true;
+   }
 }
 
 static const struct key_entry asus_nb_wmi_keymap[] = {
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index cfccfc0b8c2f..80f3447734fc 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1885,11 +1885,21 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
 * Note, on most Eeepc, there is no way to check if a method exist
 * or note, while on notebooks, they returns 0xFFFE on failure,
 * but once again, SPEC may probably be used for that kind of things.
+*
+* Additionally at least TUF Gaming series laptops return 0 for unknown
+* methods, so the detection in this way is not possible and method must
+* be forced. Likely the presence of ACPI device ASUS7000 indicates
+* this.
 */
-   if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
+   if (asus->driver->quirks->force_dsts) {
+   pr_info("DSTS method forced\n");
+   asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+   } else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
+   0, 0, NULL)) {
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
-   else
+   } else {
asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+   }
 
/* CWAP allow to define the behavior of the Fn+F2 key,
 * this method doesn't seems to be present on Eee PCs */
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 6c1311f4b04d..94056da02fde 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -54,6 +54,11 @@ struct quirk_entry {
 */
int no_display_toggle;
u32 xusb2pr;
+   /**
+* Force DSTS instead of DSCS and skip detection. Useful if WMNB
+* returns nothing on unknown method call.
+*/
+   bool force_dsts;
 
bool (*i8042_filter)(unsigned char data, unsigned char str,
 struct serio *serio);
-- 
2.17.1



Re: [PATCH 00/11] asus-wmi: Support of ASUS TUF Gaming series laptops

2019-04-10 Thread Yurii Pavlovskyi
Hi,

sorry, just realized, I've broken the logging. I will re-post patches 04 to 11 
as replies to original ones, 1 to 3 were ok.


[PATCH 11/11] platform/x86: asus-wmi: Do not disable keyboard backlight on unload

2019-04-10 Thread Yurii Pavlovskyi
The keyboard backlight is disabled when module is unloaded as it is
exposed as LED device. Change this behavior to ignore setting 0 brightness
when the ledclass device is unloading.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 941c628945ac..a0ffdd99eae2 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -475,6 +475,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, 
int value)
 static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
 {
+   /* Prevent disabling keyboard backlight on module unregister */
+   if (led_cdev->flags & LED_UNREGISTERING)
+   return;
+
do_kbd_led_set(led_cdev, value);
 }
 
-- 
2.17.1



[PATCH 10/11] platform/x86: asus-wmi: Switch fan boost mode

2019-04-10 Thread Yurii Pavlovskyi
The WMI exposes a write-only device ID where three modes can be switched
on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5
that does have a fan icon which is designed to toggle between these 3
modes.

Add a SysFS entry that reads the last written value and updates value in
WMI on write and a hotkey handler that toggles the modes. The
corresponding DEVS device handler does obviously take 3 possible
argument values.

Method (SFBM, 1, NotSerialized)
{
If ((Arg0 == Zero) { .. }
If ((Arg0 == One)) { .. }
If ((Arg0 == 0x02)) { .. }
}

... // DEVS
If ((IIA0 == 0x00110018))
{
   SFBM (IIA1)
   Return (One)
}

* 0x00 - is normal,
* 0x01 - is obviously turbo by the amount of noise, might be useful to
avoid CPU frequency throttling on high load,
* 0x02 - the meaning is unknown at the time as modes are not named
in the vendor documentation, but it does look like a quiet mode as CPU
temperature does increase about 10 degrees on maximum load.

Signed-off-by: Yurii Pavlovskyi 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  10 ++
 drivers/platform/x86/asus-wmi.c   | 119 --
 include/linux/platform_data/x86/asus-wmi.h|   1 +
 3 files changed, 117 insertions(+), 13 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 300a40519695..2b3184e297a7 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -97,3 +97,13 @@ Description:
Write changed RGB keyboard backlight parameters:
* 1 - permanently,
* 2 - temporarily.
+
+What:  /sys/devices/platform//fan_mode
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   Fan boost mode:
+   * 0 - normal,
+   * 1 - turbo,
+   * 2 - quiet?
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f4323a57f22f..941c628945ac 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -69,6 +69,7 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_KBD_BRTUP   0xc4
 #define NOTIFY_KBD_BRTDWN  0xc5
 #define NOTIFY_KBD_BRTTOGGLE   0xc7
+#define NOTIFY_KBD_FBM 0x99
 
 #define ASUS_FAN_DESC  "cpu_fan"
 #define ASUS_FAN_MFUN  0x13
@@ -77,6 +78,8 @@ MODULE_LICENSE("GPL");
 #define ASUS_FAN_CTRL_MANUAL   1
 #define ASUS_FAN_CTRL_AUTO 2
 
+#define ASUS_FAN_MODE_COUNT3
+
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
@@ -196,6 +199,9 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
 
+   bool fan_mode_available;
+   u8 fan_mode;
+
bool kbbl_rgb_available;
struct asus_kbbl_rgb kbbl_rgb;
 
@@ -1833,6 +1839,87 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
 }
 
+/* Fan mode 
***/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+   u32 result;
+   int err;
+
+   asus->fan_mode_available = false;
+
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, );
+   if (err) {
+   if (err == -ENODEV)
+   return 0;
+   else
+   return err;
+   }
+
+   if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
+   asus->fan_mode_available = true;
+
+   return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+   int err;
+   u8 value;
+   u32 retval;
+
+   value = asus->fan_mode % ASUS_FAN_MODE_COUNT;
+   pr_info(PR "Set fan mode: %u\n", value);
+   err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, );
+
+   if (err) {
+   pr_warn(PR "Failed to set fan mode: %d\n", err);
+   return err;
+   }
+
+   if (retval != 1) {
+   pr_warn(PR "Failed to set fan mode (retval): 0x%x\n", retval);
+   return -EIO;
+   }
+
+   return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+   asus->fan_mode = (asus->fan_mode + 1) % ASUS_FAN_MODE_COUNT;
+   return fan_mode_write(asus);
+}
+
+static ssize_t fan_mode_show(struct device *dev,
+   struct device_attribute *attr, char *buf)
+{
+   struct asus_wmi *asus = dev_get_drvdata(dev);
+
+   return show_u8(asus->fan_mode, buf);
+}
+
+static ssize_t fan_mode_store(struct device *dev,
+   struct device_attribute *attr, const char *buf, size_t count)
+{
+   int result;
+   u8 new_mode;
+
+   struct asus_wmi *asus = dev_get_drvdata(dev);
+
+  

[PATCH 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight

2019-04-10 Thread Yurii Pavlovskyi
The WMI exposes two methods for controlling RGB keyboard backlight which
allow to control:
* RGB components in range 00 - ff,
* Switch between 4 effects,
* Switch between 3 effect speed modes,
* Separately enable the backlight on boot, in awake state (after driver
  load), in sleep mode, and probably in something called shutdown mode
  (no observable effects of enabling it are known so far).

The configuration should be written to several sysfs parameter buffers
which are then written via WMI by writing either 1 or 2 to the "kbbl_set"
parameter. When reading the buffers the last written value is returned.

If the 2 is written to "kbbl_set", the parameters will be reset on reboot
(temporary mode), 1 is permanent mode, parameters are retained.

The calls use new 3-dword input buffer method call.

The functionality is only enabled if corresponding DSTS methods return
exact valid values.

The following script demonstrates usage:

echo Red [00 - ff]
echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red
echo Green [00 - ff]
echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green
echo Blue [00 - ff]
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue
echo Mode: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode
echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed
echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep,
echo 2a or ff to set all
echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags
echo Save: 1 - permanently, 2 - temporarily, reset after reboot
echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set

Signed-off-by: Yurii Pavlovskyi 
---
 .../ABI/testing/sysfs-platform-asus-wmi   |  61 
 drivers/platform/x86/asus-wmi.c   | 329 ++
 include/linux/platform_data/x86/asus-wmi.h|   2 +
 3 files changed, 392 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..300a40519695 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,64 @@ KernelVersion:   3.5
 Contact:   "AceLan Kao" 
 Description:
Resume on lid open. 1 means on, 0 means off.
+
+What:  /sys/devices/platform//kbbl/kbbl_red
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight red component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_green
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight green component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_blue
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight blue component: 00 .. ff.
+
+What:  /sys/devices/platform//kbbl/kbbl_mode
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight mode:
+   * 0 - static color,
+   * 1 - blink,
+   * 2 - rainbow,
+   * 3 - strobe.
+
+What:  /sys/devices/platform//kbbl/kbbl_speed
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight speed for modes 1 and 2:
+   * 0 - slow,
+   * 1 - medium,
+   * 2 - fast.
+
+What:  /sys/devices/platform//kbbl/kbbl_flags
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   RGB keyboard backlight enable flags (2a to enable everything), 
OR of:
+   * 02 - on boot (until module load),
+   * 08 - awake,
+   * 20 - sleep.
+
+What:      /sys/devices/platform//kbbl/kbbl_set
+Date:  Apr 2019
+KernelVersion: 5.1
+Contact:   "Yurii Pavlovskyi" 
+Description:
+   Write changed RGB keyboard backlight parameters:
+   * 1 - permanently,
+   * 2 - temporarily.
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 175ecd5b7c51..f4323a57f22f 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -145,6 +145,21 @@ struct asus_rfkill {
u32 dev_id;
 };
 
+struct asus_kbbl_rgb {
+   u8 kbbl_red;
+   u8 kbbl_green;
+   u8 kbbl_blue;
+   u8 kbbl_mode;
+   u8 kbbl_speed;
+
+   u8 kbbl_set_red;
+   u8 kbbl_set_green;
+   u8 kbbl_set_blue;
+   u8 kbbl_set_m

[PATCH 08/11] platform/x86: asus-wmi: Enhance detection of thermal data

2019-04-10 Thread Yurii Pavlovskyi
The obviously wrong value 1 for temperature device ID in this driver is
returned by at least some devices, including TUF Gaming series laptops,
instead of 0 as expected previously. Observable effect is that a
temp1_input in hwmon reads temperature near absolute zero.

* Consider 0.1 K as erroneous value in addition to 0 K.
* Refactor detection of thermal input availability to a separate function.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 46 -
 1 file changed, 39 insertions(+), 7 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index b9a6dc224e08..175ecd5b7c51 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -176,6 +176,7 @@ struct asus_wmi {
struct asus_rfkill gps;
struct asus_rfkill uwb;
 
+   bool asus_hwmon_thermal_available;
bool asus_hwmon_fan_manual_mode;
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
@@ -1373,6 +1374,32 @@ static struct attribute *hwmon_attributes[] = {
NULL
 };
 
+static int asus_hwmon_check_thermal_available(struct asus_wmi *asus)
+{
+   u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
+   int err;
+
+   asus->asus_hwmon_thermal_available = false;
+   err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, );
+
+   if (err < 0) {
+   if (err == -ENODEV)
+   return 0;
+
+   return err;
+   }
+
+   /*
+* If the temperature value in deci-Kelvin is near the absolute
+* zero temperature, something is clearly wrong.
+*/
+   if (!value || value == 1)
+   return 0;
+
+   asus->asus_hwmon_thermal_available = true;
+   return 0;
+}
+
 static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
  struct attribute *attr, int idx)
 {
@@ -1386,8 +1413,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject 
*kobj,
 
if (attr == _attr_pwm1.attr)
dev_id = ASUS_WMI_DEVID_FAN_CTRL;
-   else if (attr == _attr_temp1_input.attr)
-   dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
if (attr == _attr_fan1_input.attr
|| attr == _attr_fan1_label.attr
@@ -1412,15 +1437,13 @@ static umode_t asus_hwmon_sysfs_is_visible(struct 
kobject *kobj,
 * - reverved bits are non-zero
 * - sfun and presence bit are not set
 */
-   if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF8
+   if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 0xFFF8)
|| (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
ok = false;
else
ok = fan_attr <= asus->asus_hwmon_num_fans;
-   } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
-   /* If value is zero, something is clearly wrong */
-   if (!value)
-   ok = false;
+   } else if (attr == _attr_temp1_input.attr) {
+   ok = asus->asus_hwmon_thermal_available;
} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
ok = true;
} else {
@@ -1476,6 +1499,15 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
}
 
pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+
+   status = asus_hwmon_check_thermal_available(asus);
+   if (status) {
+   pr_warn("Could not check if thermal available: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info(PR "Thermal available: %d\n",
+   asus->asus_hwmon_thermal_available);
return 0;
 }
 
-- 
2.17.1



[PATCH 07/11] platform/x86: asus-wmi: Organize code into sections

2019-04-10 Thread Yurii Pavlovskyi
The driver has grown (and will more) pretty big which makes it hard to
navigate and understand. Add uniform comments to the code and ensure that
it is sorted into logical sections.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 94 -
 1 file changed, 46 insertions(+), 48 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index e0a710c64dea..b9a6dc224e08 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -191,6 +191,8 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
 };
 
+/* Input 
**/
+
 static int asus_wmi_input_init(struct asus_wmi *asus)
 {
int err;
@@ -228,6 +230,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
+/* WMI 
/
+
 static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1,
u32 arg2, u32 *retval)
 {
@@ -246,7 +250,7 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 
arg0, u32 arg1,
 , );
 
if (ACPI_FAILURE(status))
-   goto exit;
+   return -EIO;
 
obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
@@ -257,10 +261,6 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 
arg0, u32 arg1,
 
kfree(obj);
 
-exit:
-   if (ACPI_FAILURE(status))
-   return -EIO;
-
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV;
 
@@ -344,9 +344,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi 
*asus, u32 dev_id)
  ASUS_WMI_DSTS_STATUS_BIT);
 }
 
-/*
- * LEDs
- */
+/* LEDs 
***/
+
 /*
  * These functions actually update the LED's, and are called from a
  * workqueue. By doing this as separate work rather than when the LED
@@ -656,6 +655,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
return rv;
 }
 
+/* RF 
*/
 
 /*
  * PCI hotplug (for wlan rfkill)
@@ -1078,6 +1078,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
return result;
 }
 
+/* Quirks 
*/
+
 static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
 {
struct pci_dev *xhci_pdev;
@@ -1110,9 +1112,8 @@ static void asus_wmi_set_als(void)
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
 }
 
-/*
- * Hwmon device
- */
+/* Hwmon device 
***/
+
 static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
  int *speed)
 {
@@ -1388,7 +1389,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject 
*kobj,
else if (attr == _attr_temp1_input.attr)
dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
-
if (attr == _attr_fan1_input.attr
|| attr == _attr_fan1_label.attr
|| attr == _attr_pwm1.attr
@@ -1460,9 +1460,27 @@ static void asus_wmi_hwmon_exit(struct asus_wmi *asus)
}
 }
 
-/*
- * Backlight
- */
+static int asus_wmi_fan_init(struct asus_wmi *asus)
+{
+   int status;
+
+   asus->asus_hwmon_pwm = -1;
+   asus->asus_hwmon_num_fans = -1;
+   asus->asus_hwmon_fan_manual_mode = false;
+
+   status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans);
+   if (status) {
+   asus->asus_hwmon_num_fans = 0;
+   pr_warn("Could not determine number of fans: %d\n", status);
+   return -ENXIO;
+   }
+
+   pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+   return 0;
+}
+
+/* Backlight 
**/
+
 static int read_backlight_power(struct asus_wmi *asus)
 {
int ret;
@@ -1644,6 +1662,8 @@ static int is_display_toggle(int code)
return 0;
 }
 
+/* WMI events 
*/
+
 static int asus_poll_wmi_event(u32 value)
 {
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -1766,9 +1786,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi 
*asus)
return -EIO;
 }
 
-/*
- * Sys helpers
- */
+/* Sysfs 
**/
+
 static int parse_arg(const char *buf, unsigned long count, int *val)
 {
if (!count)
@@ -1907,9 +1926,8 @@ static int asus_wmi_sysfs_init(struct platform_device 
*device)
return sysfs_create_group(>dev.kobj, _attribute_group)

[PATCH 06/11] platform/x86: asus-nb-wmi: Add microphone mute key code

2019-04-10 Thread Yurii Pavlovskyi
The microphone mute key that is present on FX505GM laptop and possibly
others is missing from sparse keymap. Add the missing code.

Also comment on the fan mode switch key that has the same code as the
already used key.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index 357d273ed336..39cf447198a9 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -474,6 +474,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, },  /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
+   { KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
@@ -488,7 +489,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI 
*/
{ KE_KEY, 0x95, { KEY_MEDIA } },
-   { KE_KEY, 0x99, { KEY_PHONE } },
+   { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
-- 
2.17.1



[PATCH 05/11] platform/x86: asus-wmi: Support queued WMI event codes

2019-04-10 Thread Yurii Pavlovskyi
Event codes are expected to be polled from a queue on at least some
models.

The WMI event codes are pushed into queue based on circular buffer. After
INIT method is called ACPI code is allowed to push events into this buffer
the INIT method can not be reverted. If the module is unloaded and an
event (such as hotkey press) gets emitted before inserting it back the
events get processed delayed by one or, if the queue overflows,
additionally delayed by about 3 seconds.

Patch was tested on a newer TUF Gaming FX505GM and older K54C model.

FX505GM
Device (ATKD)
{ ..
Name (ATKQ, Package (0x10)
{
0x, ..
}

Method (IANQ, 1, Serialized)
{
If ((AQNO >= 0x10))
{
Local0 = 0x64
While ((Local0 && (AQNO >= 0x10)))
{
Local0--
Sleep (0x0A)
}
...
..
AQTI++
AQTI &= 0x0F
ATKQ [AQTI] = Arg0
...
}

Method (GANQ, 0, Serialized)
{
..
If (AQNO)
{
...
Local0 = DerefOf (ATKQ [AQHI])
AQHI++
AQHI &= 0x0F
Return (Local0)
}

Return (One)
}

This code is almost identical to K54C, which does return Ones on empty
queue.

K54C:
Method (GANQ, 0, Serialized)
{
If (AQNO)
{
...
Return (Local0)
}

Return (Ones)
}

The fix flushes the old key codes out of the queue on load and after
receiving event the queue is read until either .. or 1 is encountered.

It might be considered a minor issue and no normal user would likely to
observe this (there is little reason unloading the driver), but it does
significantly frustrate a developer who is unlucky enough to encounter
this.

Introduce functionality for flushing and processing queued codes, which is
enabled via quirk flag for ASUS7000. It might be considered if it is
reasonable to enable it everywhere (might introduce regressions) or always
try to flush the queue on module load and try to detect if this quirk is
present in the future.

This patch limits the effect to the specific hardware defined by ASUS7000
device that is used for driver detection by vendor driver of Fx505. The
fallback is also implemented in case initial flush fails.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c |   1 +
 drivers/platform/x86/asus-wmi.c| 122 ++---
 drivers/platform/x86/asus-wmi.h|   2 +
 3 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index cc5f0765a8d9..357d273ed336 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -438,6 +438,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver 
*driver)
 
if (acpi_dev_found("ASUS7000")) {
driver->quirks->force_dsts = true;
+   driver->quirks->wmi_event_queue = true;
}
 }
 
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 58890d87d50c..e0a710c64dea 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -80,6 +80,12 @@ MODULE_LICENSE("GPL");
 #define USB_INTEL_XUSB2PR  0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
 
+#define WMI_EVENT_QUEUE_SIZE   0x10
+#define WMI_EVENT_QUEUE_END0x1
+#define WMI_EVENT_MASK 0x
+/* The event value is always the same. */
+#define WMI_EVENT_VALUE0xFF
+
 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
 
 static bool ashs_present(void)
@@ -143,6 +149,7 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
+   bool wmi_event_queue;
 
struct input_dev *inputdev;
struct backlight_device *backlight_device;
@@ -1637,77 +1644,126 @@ static int is_display_toggle(int code)
return 0;
 }
 
-static void asus_wmi_notify(u32 value, void *context)
+static int asus_poll_wmi_event(u32 value)
 {
-   struct asus_wmi *asus = context;
-   struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+   struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
-   int code;
-   int orig_code;
-   unsigned int key_value = 1;
-   bool autorelease = 1;
+   int code = -EIO;
 
-   status = wmi_get_event_data(value, );
-   if (status != AE_OK) {
-   pr_err("bad event status 0x%x\n", status);
-   return;
+   status = wmi_get_event_data(value, );
+   if (ACPI_FAILURE(status)) {
+   pr_warn(PR "Failed to get WMI event code: %s\n",
+   acpi_format_exception(status));
+   return code;
}
 
-   

[PATCH 04/11] platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection

2019-04-10 Thread Yurii Pavlovskyi
The DSTS method detection fails, as nothing is returned if method is not
defined in WMNB. As a result the control of keyboard backlight is not
functional for TUF Gaming series laptops (at the time the only
functionality of the driver on this model implemented with WMI methods).

Patch was tested on a newer TUF Gaming FX505GM and older K54C model.

FX505GM:
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (Zero)
}
...
// No return
}

K54C:
Method (WMNB, 3, Serialized)
{ ...
If ((Local0 == 0x53545344))
{
...
Return (0x02)
}
...
Return (0xFFFE)
}

The non-existing method ASUS_WMI_METHODID_DSTS=0x53544344 (actually it is
DCTS in little endian ASCII) is selected in asus->dsts.

One way to fix this would be to call both for every known device ID until
some answers - this would increase module load time.

Another option is to check some device that is known to exist on every
model - none known at the time.

Last option, which is implemented, is to check for presence of the
ASUS7000 device in ACPI tree (it is a dummy device), which is the
condition used for loading the vendor driver for this model. This might
not fix every affected model ever produced, but it likely does not
introduce any regressions. The patch introduces a quirk that is enabled
when ASUS7000 is found.

Scope (_SB)
{
Device (ATK)
{
Name (_HID, "ASUS7000")  // _HID: Hardware ID
}
}

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-nb-wmi.c |  5 +
 drivers/platform/x86/asus-wmi.c| 16 +---
 drivers/platform/x86/asus-wmi.h|  5 +
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/asus-nb-wmi.c 
b/drivers/platform/x86/asus-nb-wmi.c
index b6f2ff95c3ed..cc5f0765a8d9 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -28,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "asus-wmi.h"
 
@@ -434,6 +435,10 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver 
*driver)
}
pr_info("Using i8042 filter function for receiving events\n");
}
+
+   if (acpi_dev_found("ASUS7000")) {
+   driver->quirks->force_dsts = true;
+   }
 }
 
 static const struct key_entry asus_nb_wmi_keymap[] = {
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index cfccfc0b8c2f..58890d87d50c 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -24,7 +24,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define PR KBUILD_MODNAME ": "
 
 #include 
 #include 
@@ -1885,11 +1885,21 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
 * Note, on most Eeepc, there is no way to check if a method exist
 * or note, while on notebooks, they returns 0xFFFE on failure,
 * but once again, SPEC may probably be used for that kind of things.
+*
+* Additionally at least TUF Gaming series laptops return 0 for unknown
+* methods, so the detection in this way is not possible and method must
+* be forced. Likely the presence of ACPI device ASUS7000 indicates
+* this.
 */
-   if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
+   if (asus->driver->quirks->force_dsts) {
+   pr_info(PR "DSTS method forced\n");
+   asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+   } else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
+   0, 0, NULL)) {
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
-   else
+   } else {
asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
+   }
 
/* CWAP allow to define the behavior of the Fn+F2 key,
 * this method doesn't seems to be present on Eee PCs */
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 6c1311f4b04d..94056da02fde 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -54,6 +54,11 @@ struct quirk_entry {
 */
int no_display_toggle;
u32 xusb2pr;
+   /**
+* Force DSTS instead of DSCS and skip detection. Useful if WMNB
+* returns nothing on unknown method call.
+*/
+   bool force_dsts;
 
bool (*i8042_filter)(unsigned char data, unsigned char str,
 struct serio *serio);
-- 
2.17.1



[PATCH 03/11] platform/x86: asus-wmi: Increase input buffer size of WMI methods

2019-04-10 Thread Yurii Pavlovskyi
The asus-nb-wmi driver is matched by WMI alias but fails to load on TUF
Gaming series laptops producing multiple ACPI errors in kernel log. Patch
was tested on TUF Gaming FX505GM and older K54C model.

The input buffer for WMI method invocation size is 2 dwords, whereas
3 are expected by this model.

FX505GM:
..
Method (WMNB, 3, Serialized)
{
P8XH (Zero, 0x11)
CreateDWordField (Arg2, Zero, IIA0)
CreateDWordField (Arg2, 0x04, IIA1)
CreateDWordField (Arg2, 0x08, IIA2)
Local0 = (Arg1 & 0x)
...

Compare with older K54C:
...
Method (WMNB, 3, NotSerialized)
{
CreateDWordField (Arg2, 0x00, IIA0)
CreateDWordField (Arg2, 0x04, IIA1)
Local0 = (Arg1 & 0x)
...

Increase buffer size to 3 dwords. No negative consequences of this change
are expected, as input buffer size is not verified. The original function
is replaced by a wrapper for a new method passing value 0 for the last
parameter. The new function will be used to control RGB keyboard
backlight.

Signed-off-by: Yurii Pavlovskyi 
---
One of current kernel errors:
ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Field [IIA2] at bit offset/
length 64/32 exceeds size of target Buffer (64 bits)
(20190215/dsopcode-203)
[ 4528.573948] No Local Variables are initialized for Method [WMNB]
[ 4528.573949] Initialized Arguments for Method [WMNB]:  (3 arguments
defined for method invocation)
[ 4528.573950]   Arg0:   bd1bea5a 
Integer 
[ 4528.573952]   Arg1:   d414dc53 
Integer 4E464741
[ 4528.573954]   Arg2:   fcefea4b 
Buffer(8) F0 95 08 00 00 00 00 00
[ 4528.573959] ACPI Error: Aborting method \_SB.ATKD.WMNB due to previous
error (AE_AML_BUFFER_LIMIT) (20190215/psparse-531)
[ 4528.686425] asus-nb-wmi: probe of asus-nb-wmi failed with error -5
---
 drivers/platform/x86/asus-wmi.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 0fbb947b07c4..cfccfc0b8c2f 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -95,6 +95,7 @@ static bool ashs_present(void)
 struct bios_args {
u32 arg0;
u32 arg1;
+   u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
 } __packed;
 
 /*
@@ -220,11 +221,13 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL;
 }
 
-int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1,
+   u32 arg2, u32 *retval)
 {
struct bios_args args = {
.arg0 = arg0,
.arg1 = arg1,
+   .arg2 = arg2
};
struct acpi_buffer input = { (acpi_size) sizeof(args),  };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -256,6 +259,11 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 
arg1, u32 *retval)
 
return 0;
 }
+
+int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
+{
+   return asus_wmi_evaluate_method_3dw(method_id, arg0, arg1, 0, retval);
+}
 EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
 
 static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
-- 
2.17.1



[PATCH 02/11] platform/x86: asus-wmi: Fix preserving keyboard, backlight intensity on load

2019-04-10 Thread Yurii Pavlovskyi
The error code and return value are mixed up. The intensity is always set
to 0 on load as kbd_led_read returns either 0 or negative value. To
reproduce set backlight to maximum, reload driver and try to increase it
using keyboard hotkey, the intensity will drop as a result. Correct the
implementation.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 6b736a9375ef..0fbb947b07c4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -591,8 +591,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
 
-   led_val = kbd_led_read(asus, NULL, NULL);
-   if (led_val >= 0) {
+   if (!kbd_led_read(asus, _val, NULL)) {
asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
-- 
2.17.1



[PATCH 01/11] platform/x86: asus-wmi: Fix hwmon device cleanup

2019-04-10 Thread Yurii Pavlovskyi
The asus-wmi driver does not clean up the hwmon device on exit or error.
To reproduce the bug, repeat rmmod, insmod to verify that device number
/sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Add
pointer to the device in module state and call cleanup on error.

Signed-off-by: Yurii Pavlovskyi 
---
 drivers/platform/x86/asus-wmi.c | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ee1fa93708ec..6b736a9375ef 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -145,6 +145,7 @@ struct asus_wmi {
 
struct input_dev *inputdev;
struct backlight_device *backlight_device;
+   struct device *hwmon_device;
struct platform_device *platform_device;
 
struct led_classdev wlan_led;
@@ -1432,9 +1433,19 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon);
}
+
+   asus->hwmon_device = hwmon;
return 0;
 }
 
+static void asus_wmi_hwmon_exit(struct asus_wmi *asus)
+{
+   if (asus->hwmon_device) {
+   asus_hwmon_fan_set_auto(asus);
+   hwmon_device_unregister(asus->hwmon_device);
+   }
+}
+
 /*
  * Backlight
  */
@@ -2157,6 +2168,7 @@ static int asus_wmi_add(struct platform_device *pdev)
 fail_rfkill:
asus_wmi_led_exit(asus);
 fail_leds:
+   asus_wmi_hwmon_exit(asus);
 fail_hwmon:
asus_wmi_input_exit(asus);
 fail_input:
@@ -2178,7 +2190,7 @@ static int asus_wmi_remove(struct platform_device *device)
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
asus_wmi_platform_exit(asus);
-   asus_hwmon_fan_set_auto(asus);
+   asus_wmi_hwmon_exit(asus);
 
kfree(asus);
return 0;
-- 
2.17.1



[PATCH 00/11] asus-wmi: Support of ASUS TUF Gaming series laptops

2019-04-10 Thread Yurii Pavlovskyi
Hi,

I'm new to kernel development, so first I would like to apologize in
advance for any mistakes.

The support for this laptop series is currently non-existent, as the
asus-nb-wmi driver (which is essentially configuration for asus-wmi) fails
to load and multiple ACPI errors are logged in dmesg. This patch series
adds pretty comprehenisve support for these relatively new laptops, adds
some code organization, and fixes couple of bugs in the asus-wmi module.

I have FX565GM (FX505GM) model, but I would guess that the same will
likely apply to the complete FX505 / FX705 series. I've also tested 
this on an older K54C model to ensure that it doesn't break support.
Unfortunately I don't have capacity to test this on more devices.

The patches 1 and 2 are pure bug fixes, but I can not measure relevance
for stable.

OVERALL DESIGN DECISIONS
Besides this patch, I've written experimental separate driver [1] for this
series to make it usable on my system as a DKMS module for 4.18 kernel for
the time being. One might wonder if it is more reasonable to make a new
independent module. The things to consider are that: asus-nb-wmi is
currently loaded by the WMI GUID alias, whereas the original ASUS driver
does check for the ASUS7000 device in ACPI. One should then choose
appropriate base driver instead of asus-wmi when asus-nb-wmi is loaded,
about third of the code gets duplicated in this case and the whole ends up
ugly.

Another question, does it make sense to embed RGB keyboard backlight
support in kernel code? There was discussion [2] about exposing WMI to the
userspace. The same would apply for the fan boost mode support. As I
understand as of yet it is still preferrable to support hardware features
in kernel.

NOTE ON HWMON
One open issue with the result is that hwmon device gets loaded anyway,
but it does not do anything noticeable. The heavily reduced code for the
MFUN is present in DSDT, but it either really does nothing or possibly
call something unnoticeable via DMA.

I've managed to detect that thermal sensor is not present, but the MFUN
for read fan speed does return 0 and not an error. Taking this as 
condition for disabling hwmon might intermittently break some existing
devices if the RPM is really 0 (no idea if that is really possible). One
might ponder on the better way to detect the presence of manual fan
control.

NOTE ON QUIRKS
I would speculate that the queue might be really present in many more
devices, it just didn't get noticed. Anyway after this is merged one might
consider if it is reasonable to enable it always and fallback if flush
fails. The patch does enable the new quirks only for very new models.

Regarding the DSTS force quirk, as I understand the underlying issue is a
workaround for EEEPC devices and use of DSTS is more conventional. It
might be reasonable to find a way to detect specific DSTS device ID that
is present on EEEPC instead, apply same ACPI device detection approach or
just duplicate the relevant method calls. I don't have access to such
device or it's DSDT and can't evaluate any of these options myself.

NOTE ON KEYBOARD BACKLIGHT
When the keyboard backlight is set via 0x50021 DEVID the brightness drops
slightly compared to brightness after boot. I did not found any way to
revert this. The method does set some bit in EC RAM, but this address is
not used anywhere else.  Unfortunately I wiped original OS after two hours
after unpacking.If someone can verify whether it is identical to behavior
of the vendor driver it would be appreciated.

NOTE ON UPOWER DAEMON
If you're testing with GNOME, notice that UPower does hang pretty badly if
the module is removed at runtime at least on my device. Stop it with
'systemctl stop upower' before removing the module and then restart it
again.

[1] https://github.com/hackbnw/faustus
[2] https://lwn.net/Articles/725725/

Yurii Pavlovskyi (11):
  platform/x86: asus-wmi: Fix hwmon device cleanup
  platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on
load
  platform/x86: asus-wmi: Increase input buffer size of WMI methods
  platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection
  platform/x86: asus-wmi: Support queued WMI event codes
  platform/x86: asus-nb-wmi: Add microphone mute key code
  platform/x86: asus-wmi: Organize code into sections
  platform/x86: asus-wmi: Enhance detection of thermal data
  platform/x86: asus-wmi: Control RGB keyboard backlight
  platform/x86: asus-wmi: Switch fan boost mode
  platform/x86: asus-wmi: Do not disable keyboard backlight on unload

 .../ABI/testing/sysfs-platform-asus-wmi   |  71 ++
 drivers/platform/x86/asus-nb-wmi.c|   9 +-
 drivers/platform/x86/asus-wmi.c   | 755 +++---
 drivers/platform/x86/asus-wmi.h   |   7 +
 include/linux/platform_data/x86/asus-wmi.h|   3 +
 5 files changed, 742 insertions(+), 103 deletions(-)

-- 
2.17.1