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

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86: alienware-wmi-wmax: Add HWMON support

All models with the "AWCC" WMAX device support monitoring fan speed and
temperature sensors. Expose this feature through the HWMON interface.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: linux-hwmon@vger.kernel.org
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
Link: https://lore.kernel.org/r/20250329-hwm-v7-7-a14ea39d8a94@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
d6999078 3dde0ae1

+397
+1
drivers/platform/x86/dell/Kconfig
··· 22 22 depends on DMI 23 23 depends on LEDS_CLASS 24 24 depends on NEW_LEDS 25 + depends on HWMON 25 26 help 26 27 This is a driver for controlling Alienware WMI driven features. 27 28
+396
drivers/platform/x86/dell/alienware-wmi-wmax.c
··· 10 10 11 11 #include <linux/array_size.h> 12 12 #include <linux/bitfield.h> 13 + #include <linux/bitmap.h> 13 14 #include <linux/bits.h> 14 15 #include <linux/dmi.h> 16 + #include <linux/hwmon.h> 15 17 #include <linux/moduleparam.h> 16 18 #include <linux/platform_profile.h> 19 + #include <linux/units.h> 17 20 #include <linux/wmi.h> 18 21 #include "alienware-wmi.h" 19 22 ··· 29 26 #define WMAX_METHOD_BRIGHTNESS 0x3 30 27 #define WMAX_METHOD_ZONE_CONTROL 0x4 31 28 29 + #define AWCC_METHOD_GET_FAN_SENSORS 0x13 32 30 #define AWCC_METHOD_THERMAL_INFORMATION 0x14 33 31 #define AWCC_METHOD_THERMAL_CONTROL 0x15 34 32 #define AWCC_METHOD_GAME_SHIFT_STATUS 0x25 ··· 44 40 45 41 /* Arbitrary limit based on supported models */ 46 42 #define AWCC_MAX_RES_COUNT 16 43 + #define AWCC_ID_BITMAP_SIZE (U8_MAX + 1) 44 + #define AWCC_ID_BITMAP_LONGS BITS_TO_LONGS(AWCC_ID_BITMAP_SIZE) 45 + 46 + static bool force_hwmon; 47 + module_param_unsafe(force_hwmon, bool, 0); 48 + MODULE_PARM_DESC(force_hwmon, "Force probing for HWMON support without checking if the WMI backend is available"); 47 49 48 50 static bool force_platform_profile; 49 51 module_param_unsafe(force_platform_profile, bool, 0); ··· 60 50 MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected"); 61 51 62 52 struct awcc_quirks { 53 + bool hwmon; 63 54 bool pprof; 64 55 bool gmode; 65 56 }; 66 57 67 58 static struct awcc_quirks g_series_quirks = { 59 + .hwmon = true, 68 60 .pprof = true, 69 61 .gmode = true, 70 62 }; 71 63 72 64 static struct awcc_quirks generic_quirks = { 65 + .hwmon = true, 73 66 .pprof = true, 74 67 .gmode = false, 75 68 }; ··· 170 157 }, 171 158 }; 172 159 160 + enum AWCC_GET_FAN_SENSORS_OPERATIONS { 161 + AWCC_OP_GET_TOTAL_FAN_TEMPS = 0x01, 162 + AWCC_OP_GET_FAN_TEMP_ID = 0x02, 163 + }; 164 + 173 165 enum AWCC_THERMAL_INFORMATION_OPERATIONS { 174 166 AWCC_OP_GET_SYSTEM_DESCRIPTION = 0x02, 175 167 AWCC_OP_GET_RESOURCE_ID = 0x03, 168 + AWCC_OP_GET_TEMPERATURE = 0x04, 169 + AWCC_OP_GET_FAN_RPM = 0x05, 170 + AWCC_OP_GET_FAN_MIN_RPM = 0x08, 171 + AWCC_OP_GET_FAN_MAX_RPM = 0x09, 176 172 AWCC_OP_GET_CURRENT_PROFILE = 0x0B, 177 173 }; 178 174 ··· 202 180 enum AWCC_SPECIAL_THERMAL_CODES { 203 181 AWCC_SPECIAL_PROFILE_CUSTOM = 0x00, 204 182 AWCC_SPECIAL_PROFILE_GMODE = 0xAB, 183 + }; 184 + 185 + enum AWCC_TEMP_SENSOR_TYPES { 186 + AWCC_TEMP_SENSOR_CPU = 0x01, 187 + AWCC_TEMP_SENSOR_GPU = 0x06, 205 188 }; 206 189 207 190 enum awcc_thermal_profile { ··· 245 218 u8 arg3; 246 219 }; 247 220 221 + struct awcc_fan_data { 222 + unsigned long auto_channels_temp; 223 + const char *label; 224 + u32 min_rpm; 225 + u32 max_rpm; 226 + u8 id; 227 + }; 228 + 248 229 struct awcc_priv { 249 230 struct wmi_device *wdev; 250 231 union { ··· 268 233 269 234 struct device *ppdev; 270 235 u8 supported_profiles[PLATFORM_PROFILE_LAST]; 236 + 237 + struct device *hwdev; 238 + struct awcc_fan_data **fan_data; 239 + unsigned long temp_sensors[AWCC_ID_BITMAP_LONGS]; 271 240 }; 272 241 273 242 static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = { ··· 536 497 return 0; 537 498 } 538 499 500 + static int awcc_get_fan_sensors(struct wmi_device *wdev, u8 operation, 501 + u8 fan_id, u8 index, u32 *out) 502 + { 503 + struct wmax_u32_args args = { 504 + .operation = operation, 505 + .arg1 = fan_id, 506 + .arg2 = index, 507 + .arg3 = 0, 508 + }; 509 + 510 + return awcc_wmi_command(wdev, AWCC_METHOD_GET_FAN_SENSORS, &args, out); 511 + } 512 + 539 513 static int awcc_thermal_information(struct wmi_device *wdev, u8 operation, u8 arg, 540 514 u32 *out) 541 515 { ··· 614 562 return 0; 615 563 } 616 564 565 + static int awcc_op_get_fan_rpm(struct wmi_device *wdev, u8 fan_id, u32 *out) 566 + { 567 + struct wmax_u32_args args = { 568 + .operation = AWCC_OP_GET_FAN_RPM, 569 + .arg1 = fan_id, 570 + .arg2 = 0, 571 + .arg3 = 0, 572 + }; 573 + 574 + return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out); 575 + } 576 + 577 + static int awcc_op_get_temperature(struct wmi_device *wdev, u8 temp_id, u32 *out) 578 + { 579 + struct wmax_u32_args args = { 580 + .operation = AWCC_OP_GET_TEMPERATURE, 581 + .arg1 = temp_id, 582 + .arg2 = 0, 583 + .arg3 = 0, 584 + }; 585 + 586 + return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out); 587 + } 588 + 617 589 static int awcc_op_get_current_profile(struct wmi_device *wdev, u32 *out) 618 590 { 619 591 struct wmax_u32_args args = { ··· 661 585 u32 out; 662 586 663 587 return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out); 588 + } 589 + 590 + /* 591 + * HWMON 592 + * - Provides temperature and fan speed monitoring as well as manual fan 593 + * control 594 + */ 595 + static umode_t awcc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, 596 + u32 attr, int channel) 597 + { 598 + const struct awcc_priv *priv = drvdata; 599 + unsigned int temp_count; 600 + 601 + switch (type) { 602 + case hwmon_temp: 603 + temp_count = bitmap_weight(priv->temp_sensors, AWCC_ID_BITMAP_SIZE); 604 + 605 + return channel < temp_count ? 0444 : 0; 606 + case hwmon_fan: 607 + return channel < priv->fan_count ? 0444 : 0; 608 + case hwmon_pwm: 609 + return channel < priv->fan_count ? 0444 : 0; 610 + default: 611 + return 0; 612 + } 613 + } 614 + 615 + static int awcc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 616 + u32 attr, int channel, long *val) 617 + { 618 + struct awcc_priv *priv = dev_get_drvdata(dev); 619 + const struct awcc_fan_data *fan; 620 + u32 state; 621 + int ret; 622 + u8 temp; 623 + 624 + switch (type) { 625 + case hwmon_temp: 626 + temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel); 627 + 628 + switch (attr) { 629 + case hwmon_temp_input: 630 + ret = awcc_op_get_temperature(priv->wdev, temp, &state); 631 + if (ret) 632 + return ret; 633 + 634 + *val = state * MILLIDEGREE_PER_DEGREE; 635 + break; 636 + default: 637 + return -EOPNOTSUPP; 638 + } 639 + 640 + break; 641 + case hwmon_fan: 642 + fan = priv->fan_data[channel]; 643 + 644 + switch (attr) { 645 + case hwmon_fan_input: 646 + ret = awcc_op_get_fan_rpm(priv->wdev, fan->id, &state); 647 + if (ret) 648 + return ret; 649 + 650 + *val = state; 651 + break; 652 + case hwmon_fan_min: 653 + *val = fan->min_rpm; 654 + break; 655 + case hwmon_fan_max: 656 + *val = fan->max_rpm; 657 + break; 658 + default: 659 + return -EOPNOTSUPP; 660 + } 661 + 662 + break; 663 + case hwmon_pwm: 664 + fan = priv->fan_data[channel]; 665 + 666 + switch (attr) { 667 + case hwmon_pwm_auto_channels_temp: 668 + *val = fan->auto_channels_temp; 669 + break; 670 + default: 671 + return -EOPNOTSUPP; 672 + } 673 + 674 + break; 675 + default: 676 + return -EOPNOTSUPP; 677 + } 678 + 679 + return 0; 680 + } 681 + 682 + static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, 683 + u32 attr, int channel, const char **str) 684 + { 685 + struct awcc_priv *priv = dev_get_drvdata(dev); 686 + u8 temp; 687 + 688 + switch (type) { 689 + case hwmon_temp: 690 + temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel); 691 + 692 + switch (temp) { 693 + case AWCC_TEMP_SENSOR_CPU: 694 + *str = "CPU"; 695 + break; 696 + case AWCC_TEMP_SENSOR_GPU: 697 + *str = "GPU"; 698 + break; 699 + default: 700 + *str = "Unknown"; 701 + break; 702 + } 703 + 704 + break; 705 + case hwmon_fan: 706 + *str = priv->fan_data[channel]->label; 707 + break; 708 + default: 709 + return -EOPNOTSUPP; 710 + } 711 + 712 + return 0; 713 + } 714 + 715 + static const struct hwmon_ops awcc_hwmon_ops = { 716 + .is_visible = awcc_hwmon_is_visible, 717 + .read = awcc_hwmon_read, 718 + .read_string = awcc_hwmon_read_string, 719 + }; 720 + 721 + static const struct hwmon_channel_info * const awcc_hwmon_info[] = { 722 + HWMON_CHANNEL_INFO(temp, 723 + HWMON_T_LABEL | HWMON_T_INPUT, 724 + HWMON_T_LABEL | HWMON_T_INPUT, 725 + HWMON_T_LABEL | HWMON_T_INPUT, 726 + HWMON_T_LABEL | HWMON_T_INPUT, 727 + HWMON_T_LABEL | HWMON_T_INPUT, 728 + HWMON_T_LABEL | HWMON_T_INPUT 729 + ), 730 + HWMON_CHANNEL_INFO(fan, 731 + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX, 732 + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX, 733 + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX, 734 + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX, 735 + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX, 736 + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX 737 + ), 738 + HWMON_CHANNEL_INFO(pwm, 739 + HWMON_PWM_AUTO_CHANNELS_TEMP, 740 + HWMON_PWM_AUTO_CHANNELS_TEMP, 741 + HWMON_PWM_AUTO_CHANNELS_TEMP, 742 + HWMON_PWM_AUTO_CHANNELS_TEMP, 743 + HWMON_PWM_AUTO_CHANNELS_TEMP, 744 + HWMON_PWM_AUTO_CHANNELS_TEMP 745 + ), 746 + NULL 747 + }; 748 + 749 + static const struct hwmon_chip_info awcc_hwmon_chip_info = { 750 + .ops = &awcc_hwmon_ops, 751 + .info = awcc_hwmon_info, 752 + }; 753 + 754 + static int awcc_hwmon_temps_init(struct wmi_device *wdev) 755 + { 756 + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); 757 + unsigned int i; 758 + int ret; 759 + u8 id; 760 + 761 + for (i = 0; i < priv->temp_count; i++) { 762 + /* 763 + * Temperature sensors IDs are listed after the fan IDs at 764 + * offset `fan_count` 765 + */ 766 + ret = awcc_op_get_resource_id(wdev, i + priv->fan_count, &id); 767 + if (ret) 768 + return ret; 769 + 770 + __set_bit(id, priv->temp_sensors); 771 + } 772 + 773 + return 0; 774 + } 775 + 776 + static char *awcc_get_fan_label(struct device *dev, u32 temp_count, u8 temp_id) 777 + { 778 + char *label; 779 + 780 + switch (temp_count) { 781 + case 0: 782 + label = "Independent Fan"; 783 + break; 784 + case 1: 785 + switch (temp_id) { 786 + case AWCC_TEMP_SENSOR_CPU: 787 + label = "Processor Fan"; 788 + break; 789 + case AWCC_TEMP_SENSOR_GPU: 790 + label = "Video Fan"; 791 + break; 792 + default: 793 + label = "Unknown Fan"; 794 + break; 795 + } 796 + 797 + break; 798 + default: 799 + label = "Shared Fan"; 800 + break; 801 + } 802 + 803 + return label; 804 + } 805 + 806 + static int awcc_hwmon_fans_init(struct wmi_device *wdev) 807 + { 808 + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); 809 + unsigned long fan_temps[AWCC_ID_BITMAP_LONGS]; 810 + unsigned long gather[AWCC_ID_BITMAP_LONGS]; 811 + u32 min_rpm, max_rpm, temp_count, temp_id; 812 + struct awcc_fan_data *fan_data; 813 + unsigned int i, j; 814 + char *label; 815 + int ret; 816 + u8 id; 817 + 818 + for (i = 0; i < priv->fan_count; i++) { 819 + fan_data = devm_kzalloc(&wdev->dev, sizeof(*fan_data), GFP_KERNEL); 820 + if (!fan_data) 821 + return -ENOMEM; 822 + 823 + /* 824 + * Fan IDs are listed first at offset 0 825 + */ 826 + ret = awcc_op_get_resource_id(wdev, i, &id); 827 + if (ret) 828 + return ret; 829 + 830 + ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MIN_RPM, id, 831 + &min_rpm); 832 + if (ret) 833 + return ret; 834 + 835 + ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MAX_RPM, id, 836 + &max_rpm); 837 + if (ret) 838 + return ret; 839 + 840 + ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_TOTAL_FAN_TEMPS, id, 841 + 0, &temp_count); 842 + if (ret) 843 + return ret; 844 + 845 + for (j = 0; j < temp_count; j++) { 846 + ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_FAN_TEMP_ID, 847 + id, j, &temp_id); 848 + if (ret) 849 + break; 850 + 851 + temp_id = FIELD_GET(AWCC_RESOURCE_ID_MASK, temp_id); 852 + __set_bit(temp_id, fan_temps); 853 + } 854 + 855 + label = awcc_get_fan_label(&wdev->dev, temp_count, temp_id); 856 + if (!label) 857 + return -ENOMEM; 858 + 859 + fan_data->id = id; 860 + fan_data->min_rpm = min_rpm; 861 + fan_data->max_rpm = max_rpm; 862 + fan_data->label = label; 863 + bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE); 864 + bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG); 865 + priv->fan_data[i] = fan_data; 866 + 867 + bitmap_zero(fan_temps, AWCC_ID_BITMAP_SIZE); 868 + } 869 + 870 + return 0; 871 + } 872 + 873 + static int awcc_hwmon_init(struct wmi_device *wdev) 874 + { 875 + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); 876 + int ret; 877 + 878 + priv->fan_data = devm_kcalloc(&wdev->dev, priv->fan_count, 879 + sizeof(*priv->fan_data), GFP_KERNEL); 880 + if (!priv->fan_data) 881 + return -ENOMEM; 882 + 883 + ret = awcc_hwmon_temps_init(wdev); 884 + if (ret) 885 + return ret; 886 + 887 + ret = awcc_hwmon_fans_init(wdev); 888 + if (ret) 889 + return ret; 890 + 891 + priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi", priv, 892 + &awcc_hwmon_chip_info, NULL); 893 + 894 + return PTR_ERR_OR_ZERO(priv->hwdev); 664 895 } 665 896 666 897 /* ··· 1134 751 priv->wdev = wdev; 1135 752 dev_set_drvdata(&wdev->dev, priv); 1136 753 754 + if (awcc->hwmon) { 755 + ret = awcc_hwmon_init(wdev); 756 + if (ret) 757 + return ret; 758 + } 759 + 1137 760 if (awcc->pprof) { 1138 761 ret = awcc_platform_profile_init(wdev); 1139 762 if (ret) ··· 1219 830 id = dmi_first_match(awcc_dmi_table); 1220 831 if (id) 1221 832 awcc = id->driver_data; 833 + 834 + if (force_hwmon) { 835 + if (!awcc) 836 + awcc = &empty_quirks; 837 + 838 + awcc->hwmon = true; 839 + } 1222 840 1223 841 if (force_platform_profile) { 1224 842 if (!awcc)