Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/x86: alienware-wmi-wmax: Expose GPIO debug methods

Expose GPIO control methods present on the AWCC interface through
DebugFS.

These models come with an RGB lighting STM32 MCU, which usually has two
GPIO pins with debug capabilities:

- Pin 0: Device Firmware Update mode (DFU)
- Pin 1: Negative Reset (NRST)

Suggested-by: Gabriel Marcano <gabemarcano@yahoo.com>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
Link: https://lore.kernel.org/r/20250505-awcc-gpio-v4-1-edda44c3a0dc@gmail.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Kurt Borja and committed by
Ilpo Järvinen
aee5cf93 cfd84b3f

+123 -1
+20
Documentation/ABI/testing/debugfs-alienware-wmi
··· 42 42 details. 43 43 44 44 RO 45 + 46 + What: /sys/kernel/debug/alienware-wmi-<wmi_device_name>/gpio_ctl/total_gpios 47 + Date: May 2025 48 + KernelVersion: 6.16 49 + Contact: Kurt Borja <kuurtb@gmail.com> 50 + Description: 51 + Total number of GPIO pins reported by the device. 52 + 53 + RO 54 + 55 + What: /sys/kernel/debug/alienware-wmi-<wmi_device_name>/gpio_ctl/pinX 56 + Date: May 2025 57 + KernelVersion: 6.16 58 + Contact: Kurt Borja <kuurtb@gmail.com> 59 + Description: 60 + This file controls GPIO pin X status. 61 + 62 + See Documentation/wmi/devices/alienware-wmi.rst for details. 63 + 64 + RW
+103 -1
drivers/platform/x86/dell/alienware-wmi-wmax.c
··· 38 38 #define AWCC_METHOD_GET_FAN_SENSORS 0x13 39 39 #define AWCC_METHOD_THERMAL_INFORMATION 0x14 40 40 #define AWCC_METHOD_THERMAL_CONTROL 0x15 41 + #define AWCC_METHOD_FWUP_GPIO_CONTROL 0x20 42 + #define AWCC_METHOD_READ_TOTAL_GPIOS 0x21 43 + #define AWCC_METHOD_READ_GPIO_STATUS 0x22 41 44 #define AWCC_METHOD_GAME_SHIFT_STATUS 0x25 42 45 43 46 #define AWCC_FAILURE_CODE 0xFFFFFFFF ··· 284 281 struct device *hwdev; 285 282 struct awcc_fan_data **fan_data; 286 283 unsigned long temp_sensors[AWCC_ID_BITMAP_LONGS]; 284 + 285 + u32 gpio_count; 287 286 }; 288 287 289 288 static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = { ··· 574 569 }; 575 570 576 571 return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out); 572 + } 573 + 574 + static int awcc_fwup_gpio_control(struct wmi_device *wdev, u8 pin, u8 status) 575 + { 576 + struct wmax_u32_args args = { 577 + .operation = pin, 578 + .arg1 = status, 579 + .arg2 = 0, 580 + .arg3 = 0, 581 + }; 582 + u32 out; 583 + 584 + return awcc_wmi_command(wdev, AWCC_METHOD_FWUP_GPIO_CONTROL, &args, &out); 585 + } 586 + 587 + static int awcc_read_total_gpios(struct wmi_device *wdev, u32 *count) 588 + { 589 + struct wmax_u32_args args = {}; 590 + 591 + return awcc_wmi_command(wdev, AWCC_METHOD_READ_TOTAL_GPIOS, &args, count); 592 + } 593 + 594 + static int awcc_read_gpio_status(struct wmi_device *wdev, u8 pin, u32 *status) 595 + { 596 + struct wmax_u32_args args = { 597 + .operation = pin, 598 + .arg1 = 0, 599 + .arg2 = 0, 600 + .arg3 = 0, 601 + }; 602 + 603 + return awcc_wmi_command(wdev, AWCC_METHOD_READ_GPIO_STATUS, &args, status); 577 604 } 578 605 579 606 static int awcc_game_shift_status(struct wmi_device *wdev, u8 operation, ··· 1354 1317 return 0; 1355 1318 } 1356 1319 1320 + static int awcc_gpio_pin_show(struct seq_file *seq, void *data) 1321 + { 1322 + unsigned long pin = debugfs_get_aux_num(seq->file); 1323 + struct wmi_device *wdev = seq->private; 1324 + u32 status; 1325 + int ret; 1326 + 1327 + ret = awcc_read_gpio_status(wdev, pin, &status); 1328 + if (ret) 1329 + return ret; 1330 + 1331 + seq_printf(seq, "%u\n", status); 1332 + 1333 + return 0; 1334 + } 1335 + 1336 + static ssize_t awcc_gpio_pin_write(struct file *file, const char __user *buf, 1337 + size_t count, loff_t *ppos) 1338 + { 1339 + unsigned long pin = debugfs_get_aux_num(file); 1340 + struct seq_file *seq = file->private_data; 1341 + struct wmi_device *wdev = seq->private; 1342 + bool status; 1343 + int ret; 1344 + 1345 + if (!ppos || *ppos) 1346 + return -EINVAL; 1347 + 1348 + ret = kstrtobool_from_user(buf, count, &status); 1349 + if (ret) 1350 + return ret; 1351 + 1352 + ret = awcc_fwup_gpio_control(wdev, pin, status); 1353 + if (ret) 1354 + return ret; 1355 + 1356 + return count; 1357 + } 1358 + 1359 + DEFINE_SHOW_STORE_ATTRIBUTE(awcc_gpio_pin); 1360 + 1357 1361 static void awcc_debugfs_remove(void *data) 1358 1362 { 1359 1363 struct dentry *root = data; ··· 1404 1326 1405 1327 static void awcc_debugfs_init(struct wmi_device *wdev) 1406 1328 { 1407 - struct dentry *root; 1329 + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); 1330 + struct dentry *root, *gpio_ctl; 1331 + u32 gpio_count; 1408 1332 char name[64]; 1333 + int ret; 1409 1334 1410 1335 scnprintf(name, sizeof(name), "%s-%s", "alienware-wmi", dev_name(&wdev->dev)); 1411 1336 root = debugfs_create_dir(name, NULL); ··· 1424 1343 debugfs_create_devm_seqfile(&wdev->dev, "pprof_data", root, 1425 1344 awcc_debugfs_pprof_data_read); 1426 1345 1346 + ret = awcc_read_total_gpios(wdev, &gpio_count); 1347 + if (ret) { 1348 + dev_dbg(&wdev->dev, "Failed to get total GPIO Pin count\n"); 1349 + goto out_add_action; 1350 + } else if (gpio_count > AWCC_MAX_RES_COUNT) { 1351 + dev_dbg(&wdev->dev, "Reported GPIO Pin count may be incorrect: %u\n", gpio_count); 1352 + goto out_add_action; 1353 + } 1354 + 1355 + gpio_ctl = debugfs_create_dir("gpio_ctl", root); 1356 + 1357 + priv->gpio_count = gpio_count; 1358 + debugfs_create_u32("total_gpios", 0444, gpio_ctl, &priv->gpio_count); 1359 + 1360 + for (unsigned int i = 0; i < gpio_count; i++) { 1361 + scnprintf(name, sizeof(name), "pin%u", i); 1362 + debugfs_create_file_aux_num(name, 0644, gpio_ctl, wdev, i, 1363 + &awcc_gpio_pin_fops); 1364 + } 1365 + 1366 + out_add_action: 1427 1367 devm_add_action_or_reset(&wdev->dev, awcc_debugfs_remove, root); 1428 1368 } 1429 1369