18cc2c415SKurt Borja // SPDX-License-Identifier: GPL-2.0-or-later
28cc2c415SKurt Borja /*
38cc2c415SKurt Borja * Alienware WMAX WMI device driver
48cc2c415SKurt Borja *
58cc2c415SKurt Borja * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
68cc2c415SKurt Borja * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
78cc2c415SKurt Borja */
88cc2c415SKurt Borja
98cc2c415SKurt Borja #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108cc2c415SKurt Borja
1132b6372dSKurt Borja #include <linux/array_size.h>
128cc2c415SKurt Borja #include <linux/bitfield.h>
13d6999078SKurt Borja #include <linux/bitmap.h>
148cc2c415SKurt Borja #include <linux/bits.h>
15b028fb49SKurt Borja #include <linux/debugfs.h>
168cc2c415SKurt Borja #include <linux/dmi.h>
17d6999078SKurt Borja #include <linux/hwmon.h>
1807ac2759SKurt Borja #include <linux/hwmon-sysfs.h>
1907ac2759SKurt Borja #include <linux/kstrtox.h>
2007ac2759SKurt Borja #include <linux/minmax.h>
218cc2c415SKurt Borja #include <linux/moduleparam.h>
228cc2c415SKurt Borja #include <linux/platform_profile.h>
2307ac2759SKurt Borja #include <linux/pm.h>
24b028fb49SKurt Borja #include <linux/seq_file.h>
25d6999078SKurt Borja #include <linux/units.h>
268cc2c415SKurt Borja #include <linux/wmi.h>
278cc2c415SKurt Borja #include "alienware-wmi.h"
288cc2c415SKurt Borja
298cc2c415SKurt Borja #define WMAX_METHOD_HDMI_SOURCE 0x1
308cc2c415SKurt Borja #define WMAX_METHOD_HDMI_STATUS 0x2
318cc2c415SKurt Borja #define WMAX_METHOD_HDMI_CABLE 0x5
328cc2c415SKurt Borja #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
338cc2c415SKurt Borja #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
348cc2c415SKurt Borja #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
358cc2c415SKurt Borja #define WMAX_METHOD_BRIGHTNESS 0x3
368cc2c415SKurt Borja #define WMAX_METHOD_ZONE_CONTROL 0x4
378cc2c415SKurt Borja
38d6999078SKurt Borja #define AWCC_METHOD_GET_FAN_SENSORS 0x13
398a1a0fb5SKurt Borja #define AWCC_METHOD_THERMAL_INFORMATION 0x14
408a1a0fb5SKurt Borja #define AWCC_METHOD_THERMAL_CONTROL 0x15
41aee5cf93SKurt Borja #define AWCC_METHOD_FWUP_GPIO_CONTROL 0x20
42aee5cf93SKurt Borja #define AWCC_METHOD_READ_TOTAL_GPIOS 0x21
43aee5cf93SKurt Borja #define AWCC_METHOD_READ_GPIO_STATUS 0x22
448a1a0fb5SKurt Borja #define AWCC_METHOD_GAME_SHIFT_STATUS 0x25
458cc2c415SKurt Borja
468a1a0fb5SKurt Borja #define AWCC_FAILURE_CODE 0xFFFFFFFF
4745983d19SKurt Borja #define AWCC_FAILURE_CODE_2 0xFFFFFFFE
48a000da9dSKurt Borja
49a000da9dSKurt Borja #define AWCC_SENSOR_ID_FLAG BIT(8)
508a1a0fb5SKurt Borja #define AWCC_THERMAL_MODE_MASK GENMASK(3, 0)
51a000da9dSKurt Borja #define AWCC_THERMAL_TABLE_MASK GENMASK(7, 4)
52a000da9dSKurt Borja #define AWCC_RESOURCE_ID_MASK GENMASK(7, 0)
538cc2c415SKurt Borja
5432b6372dSKurt Borja /* Arbitrary limit based on supported models */
5532b6372dSKurt Borja #define AWCC_MAX_RES_COUNT 16
56d6999078SKurt Borja #define AWCC_ID_BITMAP_SIZE (U8_MAX + 1)
57d6999078SKurt Borja #define AWCC_ID_BITMAP_LONGS BITS_TO_LONGS(AWCC_ID_BITMAP_SIZE)
58d6999078SKurt Borja
59d6999078SKurt Borja static bool force_hwmon;
60d6999078SKurt Borja module_param_unsafe(force_hwmon, bool, 0);
61d6999078SKurt Borja MODULE_PARM_DESC(force_hwmon, "Force probing for HWMON support without checking if the WMI backend is available");
6232b6372dSKurt Borja
638cc2c415SKurt Borja static bool force_platform_profile;
648cc2c415SKurt Borja module_param_unsafe(force_platform_profile, bool, 0);
658cc2c415SKurt Borja MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
668cc2c415SKurt Borja
678cc2c415SKurt Borja static bool force_gmode;
688cc2c415SKurt Borja module_param_unsafe(force_gmode, bool, 0);
698cc2c415SKurt Borja MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
708cc2c415SKurt Borja
718cc2c415SKurt Borja struct awcc_quirks {
72d6999078SKurt Borja bool hwmon;
738cc2c415SKurt Borja bool pprof;
748cc2c415SKurt Borja bool gmode;
758cc2c415SKurt Borja };
768cc2c415SKurt Borja
778cc2c415SKurt Borja static struct awcc_quirks g_series_quirks = {
78d6999078SKurt Borja .hwmon = true,
798cc2c415SKurt Borja .pprof = true,
808cc2c415SKurt Borja .gmode = true,
818cc2c415SKurt Borja };
828cc2c415SKurt Borja
838cc2c415SKurt Borja static struct awcc_quirks generic_quirks = {
84d6999078SKurt Borja .hwmon = true,
858cc2c415SKurt Borja .pprof = true,
868cc2c415SKurt Borja .gmode = false,
878cc2c415SKurt Borja };
888cc2c415SKurt Borja
898cc2c415SKurt Borja static struct awcc_quirks empty_quirks;
908cc2c415SKurt Borja
918cc2c415SKurt Borja static const struct dmi_system_id awcc_dmi_table[] __initconst = {
928cc2c415SKurt Borja {
93202a8612SKurt Borja .ident = "Alienware Area-51m R2",
94202a8612SKurt Borja .matches = {
95202a8612SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
96202a8612SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware Area-51m R2"),
97202a8612SKurt Borja },
98202a8612SKurt Borja .driver_data = &generic_quirks,
99202a8612SKurt Borja },
100202a8612SKurt Borja {
101246f9bb6SKurt Borja .ident = "Alienware m15 R7",
102246f9bb6SKurt Borja .matches = {
103246f9bb6SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
104246f9bb6SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m15 R7"),
105246f9bb6SKurt Borja },
106246f9bb6SKurt Borja .driver_data = &generic_quirks,
107246f9bb6SKurt Borja },
108246f9bb6SKurt Borja {
109202a8612SKurt Borja .ident = "Alienware m16 R1",
110202a8612SKurt Borja .matches = {
111202a8612SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
112202a8612SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1"),
113202a8612SKurt Borja },
114202a8612SKurt Borja .driver_data = &g_series_quirks,
115202a8612SKurt Borja },
116202a8612SKurt Borja {
1178cc2c415SKurt Borja .ident = "Alienware m16 R1 AMD",
1188cc2c415SKurt Borja .matches = {
1198cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1208cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"),
1218cc2c415SKurt Borja },
122*e2468dc7SKurt Borja .driver_data = &generic_quirks,
1238cc2c415SKurt Borja },
1248cc2c415SKurt Borja {
125202a8612SKurt Borja .ident = "Alienware m16 R2",
126202a8612SKurt Borja .matches = {
127202a8612SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
128202a8612SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R2"),
129202a8612SKurt Borja },
1308cc2c415SKurt Borja .driver_data = &generic_quirks,
1318cc2c415SKurt Borja },
1328cc2c415SKurt Borja {
1338cc2c415SKurt Borja .ident = "Alienware m17 R5",
1348cc2c415SKurt Borja .matches = {
1358cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1368cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
1378cc2c415SKurt Borja },
1388cc2c415SKurt Borja .driver_data = &generic_quirks,
1398cc2c415SKurt Borja },
1408cc2c415SKurt Borja {
1418cc2c415SKurt Borja .ident = "Alienware m18 R2",
1428cc2c415SKurt Borja .matches = {
1438cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1448cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"),
1458cc2c415SKurt Borja },
1468cc2c415SKurt Borja .driver_data = &generic_quirks,
1478cc2c415SKurt Borja },
1488cc2c415SKurt Borja {
1498cc2c415SKurt Borja .ident = "Alienware x15 R1",
1508cc2c415SKurt Borja .matches = {
1518cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1528cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"),
1538cc2c415SKurt Borja },
1548cc2c415SKurt Borja .driver_data = &generic_quirks,
1558cc2c415SKurt Borja },
1568cc2c415SKurt Borja {
157202a8612SKurt Borja .ident = "Alienware x15 R2",
158202a8612SKurt Borja .matches = {
159202a8612SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
160202a8612SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R2"),
161202a8612SKurt Borja },
162202a8612SKurt Borja .driver_data = &generic_quirks,
163202a8612SKurt Borja },
164202a8612SKurt Borja {
1658cc2c415SKurt Borja .ident = "Alienware x17 R2",
1668cc2c415SKurt Borja .matches = {
1678cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1688cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"),
1698cc2c415SKurt Borja },
1708cc2c415SKurt Borja .driver_data = &generic_quirks,
1718cc2c415SKurt Borja },
1728cc2c415SKurt Borja {
1738cc2c415SKurt Borja .ident = "Dell Inc. G15 5510",
1748cc2c415SKurt Borja .matches = {
1758cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1768cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"),
1778cc2c415SKurt Borja },
1788cc2c415SKurt Borja .driver_data = &g_series_quirks,
1798cc2c415SKurt Borja },
1808cc2c415SKurt Borja {
1818cc2c415SKurt Borja .ident = "Dell Inc. G15 5511",
1828cc2c415SKurt Borja .matches = {
1838cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1848cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"),
1858cc2c415SKurt Borja },
1868cc2c415SKurt Borja .driver_data = &g_series_quirks,
1878cc2c415SKurt Borja },
1888cc2c415SKurt Borja {
1898cc2c415SKurt Borja .ident = "Dell Inc. G15 5515",
1908cc2c415SKurt Borja .matches = {
1918cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1928cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
1938cc2c415SKurt Borja },
1948cc2c415SKurt Borja .driver_data = &g_series_quirks,
1958cc2c415SKurt Borja },
1968cc2c415SKurt Borja {
197202a8612SKurt Borja .ident = "Dell Inc. G16 7630",
198202a8612SKurt Borja .matches = {
199202a8612SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
200202a8612SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "Dell G16 7630"),
201202a8612SKurt Borja },
202202a8612SKurt Borja .driver_data = &g_series_quirks,
203202a8612SKurt Borja },
204202a8612SKurt Borja {
2058cc2c415SKurt Borja .ident = "Dell Inc. G3 3500",
2068cc2c415SKurt Borja .matches = {
2078cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
2088cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"),
2098cc2c415SKurt Borja },
2108cc2c415SKurt Borja .driver_data = &g_series_quirks,
2118cc2c415SKurt Borja },
2128cc2c415SKurt Borja {
2138cc2c415SKurt Borja .ident = "Dell Inc. G3 3590",
2148cc2c415SKurt Borja .matches = {
2158cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
2168cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"),
2178cc2c415SKurt Borja },
2188cc2c415SKurt Borja .driver_data = &g_series_quirks,
2198cc2c415SKurt Borja },
2208cc2c415SKurt Borja {
2218cc2c415SKurt Borja .ident = "Dell Inc. G5 5500",
2228cc2c415SKurt Borja .matches = {
2238cc2c415SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
2248cc2c415SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"),
2258cc2c415SKurt Borja },
2268cc2c415SKurt Borja .driver_data = &g_series_quirks,
2278cc2c415SKurt Borja },
228202a8612SKurt Borja {
229202a8612SKurt Borja .ident = "Dell Inc. G5 5505",
230202a8612SKurt Borja .matches = {
231202a8612SKurt Borja DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
232202a8612SKurt Borja DMI_MATCH(DMI_PRODUCT_NAME, "G5 5505"),
233202a8612SKurt Borja },
234202a8612SKurt Borja .driver_data = &g_series_quirks,
235202a8612SKurt Borja },
2368cc2c415SKurt Borja };
2378cc2c415SKurt Borja
238d6999078SKurt Borja enum AWCC_GET_FAN_SENSORS_OPERATIONS {
239d6999078SKurt Borja AWCC_OP_GET_TOTAL_FAN_TEMPS = 0x01,
240d6999078SKurt Borja AWCC_OP_GET_FAN_TEMP_ID = 0x02,
241d6999078SKurt Borja };
242d6999078SKurt Borja
2438a1a0fb5SKurt Borja enum AWCC_THERMAL_INFORMATION_OPERATIONS {
2448a1a0fb5SKurt Borja AWCC_OP_GET_SYSTEM_DESCRIPTION = 0x02,
2458a1a0fb5SKurt Borja AWCC_OP_GET_RESOURCE_ID = 0x03,
246d6999078SKurt Borja AWCC_OP_GET_TEMPERATURE = 0x04,
247d6999078SKurt Borja AWCC_OP_GET_FAN_RPM = 0x05,
248d6999078SKurt Borja AWCC_OP_GET_FAN_MIN_RPM = 0x08,
249d6999078SKurt Borja AWCC_OP_GET_FAN_MAX_RPM = 0x09,
2508a1a0fb5SKurt Borja AWCC_OP_GET_CURRENT_PROFILE = 0x0B,
25107ac2759SKurt Borja AWCC_OP_GET_FAN_BOOST = 0x0C,
2528cc2c415SKurt Borja };
2538cc2c415SKurt Borja
2548a1a0fb5SKurt Borja enum AWCC_THERMAL_CONTROL_OPERATIONS {
2558a1a0fb5SKurt Borja AWCC_OP_ACTIVATE_PROFILE = 0x01,
25607ac2759SKurt Borja AWCC_OP_SET_FAN_BOOST = 0x02,
2578cc2c415SKurt Borja };
2588cc2c415SKurt Borja
2598a1a0fb5SKurt Borja enum AWCC_GAME_SHIFT_STATUS_OPERATIONS {
2608a1a0fb5SKurt Borja AWCC_OP_TOGGLE_GAME_SHIFT = 0x01,
2618a1a0fb5SKurt Borja AWCC_OP_GET_GAME_SHIFT_STATUS = 0x02,
2628cc2c415SKurt Borja };
2638cc2c415SKurt Borja
2648a1a0fb5SKurt Borja enum AWCC_THERMAL_TABLES {
265a000da9dSKurt Borja AWCC_THERMAL_TABLE_LEGACY = 0x9,
266a000da9dSKurt Borja AWCC_THERMAL_TABLE_USTT = 0xA,
2678cc2c415SKurt Borja };
2688cc2c415SKurt Borja
2693dde0ae1SKurt Borja enum AWCC_SPECIAL_THERMAL_CODES {
2703dde0ae1SKurt Borja AWCC_SPECIAL_PROFILE_CUSTOM = 0x00,
2713dde0ae1SKurt Borja AWCC_SPECIAL_PROFILE_GMODE = 0xAB,
2723dde0ae1SKurt Borja };
2733dde0ae1SKurt Borja
274d6999078SKurt Borja enum AWCC_TEMP_SENSOR_TYPES {
275d6999078SKurt Borja AWCC_TEMP_SENSOR_CPU = 0x01,
276d6999078SKurt Borja AWCC_TEMP_SENSOR_GPU = 0x06,
277d6999078SKurt Borja };
278d6999078SKurt Borja
2798a1a0fb5SKurt Borja enum awcc_thermal_profile {
2808a1a0fb5SKurt Borja AWCC_PROFILE_USTT_BALANCED,
2818a1a0fb5SKurt Borja AWCC_PROFILE_USTT_BALANCED_PERFORMANCE,
2828a1a0fb5SKurt Borja AWCC_PROFILE_USTT_COOL,
2838a1a0fb5SKurt Borja AWCC_PROFILE_USTT_QUIET,
2848a1a0fb5SKurt Borja AWCC_PROFILE_USTT_PERFORMANCE,
2858a1a0fb5SKurt Borja AWCC_PROFILE_USTT_LOW_POWER,
2868a1a0fb5SKurt Borja AWCC_PROFILE_LEGACY_QUIET,
2878a1a0fb5SKurt Borja AWCC_PROFILE_LEGACY_BALANCED,
2888a1a0fb5SKurt Borja AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE,
2898a1a0fb5SKurt Borja AWCC_PROFILE_LEGACY_PERFORMANCE,
2908a1a0fb5SKurt Borja AWCC_PROFILE_LAST,
2918cc2c415SKurt Borja };
2928cc2c415SKurt Borja
2938cc2c415SKurt Borja struct wmax_led_args {
2948cc2c415SKurt Borja u32 led_mask;
2958cc2c415SKurt Borja struct color_platform colors;
2968cc2c415SKurt Borja u8 state;
2978cc2c415SKurt Borja } __packed;
2988cc2c415SKurt Borja
2998cc2c415SKurt Borja struct wmax_brightness_args {
3008cc2c415SKurt Borja u32 led_mask;
3018cc2c415SKurt Borja u32 percentage;
3028cc2c415SKurt Borja };
3038cc2c415SKurt Borja
3048cc2c415SKurt Borja struct wmax_basic_args {
3058cc2c415SKurt Borja u8 arg;
3068cc2c415SKurt Borja };
3078cc2c415SKurt Borja
3088cc2c415SKurt Borja struct wmax_u32_args {
3098cc2c415SKurt Borja u8 operation;
3108cc2c415SKurt Borja u8 arg1;
3118cc2c415SKurt Borja u8 arg2;
3128cc2c415SKurt Borja u8 arg3;
3138cc2c415SKurt Borja };
3148cc2c415SKurt Borja
315d6999078SKurt Borja struct awcc_fan_data {
316d6999078SKurt Borja unsigned long auto_channels_temp;
317d6999078SKurt Borja const char *label;
318d6999078SKurt Borja u32 min_rpm;
319d6999078SKurt Borja u32 max_rpm;
32007ac2759SKurt Borja u8 suspend_cache;
321d6999078SKurt Borja u8 id;
322d6999078SKurt Borja };
323d6999078SKurt Borja
3248cc2c415SKurt Borja struct awcc_priv {
3258cc2c415SKurt Borja struct wmi_device *wdev;
32632b6372dSKurt Borja union {
32732b6372dSKurt Borja u32 system_description;
32832b6372dSKurt Borja struct {
32932b6372dSKurt Borja u8 fan_count;
33032b6372dSKurt Borja u8 temp_count;
33132b6372dSKurt Borja u8 unknown_count;
33232b6372dSKurt Borja u8 profile_count;
33332b6372dSKurt Borja };
33432b6372dSKurt Borja u8 res_count[4];
33532b6372dSKurt Borja };
33632b6372dSKurt Borja
3378cc2c415SKurt Borja struct device *ppdev;
33877bb2ec5SKurt Borja u8 supported_profiles[PLATFORM_PROFILE_LAST];
339d6999078SKurt Borja
340d6999078SKurt Borja struct device *hwdev;
341d6999078SKurt Borja struct awcc_fan_data **fan_data;
342d6999078SKurt Borja unsigned long temp_sensors[AWCC_ID_BITMAP_LONGS];
343aee5cf93SKurt Borja
344aee5cf93SKurt Borja u32 gpio_count;
3458cc2c415SKurt Borja };
3468cc2c415SKurt Borja
3478a1a0fb5SKurt Borja static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = {
3488a1a0fb5SKurt Borja [AWCC_PROFILE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
3498a1a0fb5SKurt Borja [AWCC_PROFILE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
3508a1a0fb5SKurt Borja [AWCC_PROFILE_USTT_COOL] = PLATFORM_PROFILE_COOL,
3518a1a0fb5SKurt Borja [AWCC_PROFILE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
3528a1a0fb5SKurt Borja [AWCC_PROFILE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
3538a1a0fb5SKurt Borja [AWCC_PROFILE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
3548a1a0fb5SKurt Borja [AWCC_PROFILE_LEGACY_QUIET] = PLATFORM_PROFILE_QUIET,
3558a1a0fb5SKurt Borja [AWCC_PROFILE_LEGACY_BALANCED] = PLATFORM_PROFILE_BALANCED,
3568a1a0fb5SKurt Borja [AWCC_PROFILE_LEGACY_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
3578a1a0fb5SKurt Borja [AWCC_PROFILE_LEGACY_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
3588cc2c415SKurt Borja };
3598cc2c415SKurt Borja
3608cc2c415SKurt Borja static struct awcc_quirks *awcc;
3618cc2c415SKurt Borja
3628cc2c415SKurt Borja /*
3638cc2c415SKurt Borja * The HDMI mux sysfs node indicates the status of the HDMI input mux.
3648cc2c415SKurt Borja * It can toggle between standard system GPU output and HDMI input.
3658cc2c415SKurt Borja */
cable_show(struct device * dev,struct device_attribute * attr,char * buf)3668cc2c415SKurt Borja static ssize_t cable_show(struct device *dev, struct device_attribute *attr,
3678cc2c415SKurt Borja char *buf)
3688cc2c415SKurt Borja {
3698cc2c415SKurt Borja struct alienfx_platdata *pdata = dev_get_platdata(dev);
3708cc2c415SKurt Borja struct wmax_basic_args in_args = {
3718cc2c415SKurt Borja .arg = 0,
3728cc2c415SKurt Borja };
3738cc2c415SKurt Borja u32 out_data;
3748cc2c415SKurt Borja int ret;
3758cc2c415SKurt Borja
3768cc2c415SKurt Borja ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
3778cc2c415SKurt Borja &in_args, sizeof(in_args), &out_data);
3788cc2c415SKurt Borja if (!ret) {
3798cc2c415SKurt Borja if (out_data == 0)
3808cc2c415SKurt Borja return sysfs_emit(buf, "[unconnected] connected unknown\n");
3818cc2c415SKurt Borja else if (out_data == 1)
3828cc2c415SKurt Borja return sysfs_emit(buf, "unconnected [connected] unknown\n");
3838cc2c415SKurt Borja }
3848cc2c415SKurt Borja
3858cc2c415SKurt Borja pr_err("alienware-wmi: unknown HDMI cable status: %d\n", ret);
3868cc2c415SKurt Borja return sysfs_emit(buf, "unconnected connected [unknown]\n");
3878cc2c415SKurt Borja }
3888cc2c415SKurt Borja
source_show(struct device * dev,struct device_attribute * attr,char * buf)3898cc2c415SKurt Borja static ssize_t source_show(struct device *dev, struct device_attribute *attr,
3908cc2c415SKurt Borja char *buf)
3918cc2c415SKurt Borja {
3928cc2c415SKurt Borja struct alienfx_platdata *pdata = dev_get_platdata(dev);
3938cc2c415SKurt Borja struct wmax_basic_args in_args = {
3948cc2c415SKurt Borja .arg = 0,
3958cc2c415SKurt Borja };
3968cc2c415SKurt Borja u32 out_data;
3978cc2c415SKurt Borja int ret;
3988cc2c415SKurt Borja
3998cc2c415SKurt Borja ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
4008cc2c415SKurt Borja &in_args, sizeof(in_args), &out_data);
4018cc2c415SKurt Borja if (!ret) {
4028cc2c415SKurt Borja if (out_data == 1)
4038cc2c415SKurt Borja return sysfs_emit(buf, "[input] gpu unknown\n");
4048cc2c415SKurt Borja else if (out_data == 2)
4058cc2c415SKurt Borja return sysfs_emit(buf, "input [gpu] unknown\n");
4068cc2c415SKurt Borja }
4078cc2c415SKurt Borja
4088cc2c415SKurt Borja pr_err("alienware-wmi: unknown HDMI source status: %u\n", ret);
4098cc2c415SKurt Borja return sysfs_emit(buf, "input gpu [unknown]\n");
4108cc2c415SKurt Borja }
4118cc2c415SKurt Borja
source_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4128cc2c415SKurt Borja static ssize_t source_store(struct device *dev, struct device_attribute *attr,
4138cc2c415SKurt Borja const char *buf, size_t count)
4148cc2c415SKurt Borja {
4158cc2c415SKurt Borja struct alienfx_platdata *pdata = dev_get_platdata(dev);
4168cc2c415SKurt Borja struct wmax_basic_args args;
4178cc2c415SKurt Borja int ret;
4188cc2c415SKurt Borja
4198cc2c415SKurt Borja if (strcmp(buf, "gpu\n") == 0)
4208cc2c415SKurt Borja args.arg = 1;
4218cc2c415SKurt Borja else if (strcmp(buf, "input\n") == 0)
4228cc2c415SKurt Borja args.arg = 2;
4238cc2c415SKurt Borja else
4248cc2c415SKurt Borja args.arg = 3;
4258cc2c415SKurt Borja pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
4268cc2c415SKurt Borja
4278cc2c415SKurt Borja ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args,
4288cc2c415SKurt Borja sizeof(args), NULL);
4298cc2c415SKurt Borja if (ret < 0)
4308cc2c415SKurt Borja pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret);
4318cc2c415SKurt Borja
4328cc2c415SKurt Borja return count;
4338cc2c415SKurt Borja }
4348cc2c415SKurt Borja
4358cc2c415SKurt Borja static DEVICE_ATTR_RO(cable);
4368cc2c415SKurt Borja static DEVICE_ATTR_RW(source);
4378cc2c415SKurt Borja
hdmi_group_visible(struct kobject * kobj)4388cc2c415SKurt Borja static bool hdmi_group_visible(struct kobject *kobj)
4398cc2c415SKurt Borja {
4408cc2c415SKurt Borja return alienware_interface == WMAX && alienfx->hdmi_mux;
4418cc2c415SKurt Borja }
4428cc2c415SKurt Borja DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
4438cc2c415SKurt Borja
4448cc2c415SKurt Borja static struct attribute *hdmi_attrs[] = {
4458cc2c415SKurt Borja &dev_attr_cable.attr,
4468cc2c415SKurt Borja &dev_attr_source.attr,
4478cc2c415SKurt Borja NULL,
4488cc2c415SKurt Borja };
4498cc2c415SKurt Borja
4508cc2c415SKurt Borja const struct attribute_group wmax_hdmi_attribute_group = {
4518cc2c415SKurt Borja .name = "hdmi",
4528cc2c415SKurt Borja .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
4538cc2c415SKurt Borja .attrs = hdmi_attrs,
4548cc2c415SKurt Borja };
4558cc2c415SKurt Borja
4568cc2c415SKurt Borja /*
4578cc2c415SKurt Borja * Alienware GFX amplifier support
4588cc2c415SKurt Borja * - Currently supports reading cable status
4598cc2c415SKurt Borja * - Leaving expansion room to possibly support dock/undock events later
4608cc2c415SKurt Borja */
status_show(struct device * dev,struct device_attribute * attr,char * buf)4618cc2c415SKurt Borja static ssize_t status_show(struct device *dev, struct device_attribute *attr,
4628cc2c415SKurt Borja char *buf)
4638cc2c415SKurt Borja {
4648cc2c415SKurt Borja struct alienfx_platdata *pdata = dev_get_platdata(dev);
4658cc2c415SKurt Borja struct wmax_basic_args in_args = {
4668cc2c415SKurt Borja .arg = 0,
4678cc2c415SKurt Borja };
4688cc2c415SKurt Borja u32 out_data;
4698cc2c415SKurt Borja int ret;
4708cc2c415SKurt Borja
4718cc2c415SKurt Borja ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
4728cc2c415SKurt Borja &in_args, sizeof(in_args), &out_data);
4738cc2c415SKurt Borja if (!ret) {
4748cc2c415SKurt Borja if (out_data == 0)
4758cc2c415SKurt Borja return sysfs_emit(buf, "[unconnected] connected unknown\n");
4768cc2c415SKurt Borja else if (out_data == 1)
4778cc2c415SKurt Borja return sysfs_emit(buf, "unconnected [connected] unknown\n");
4788cc2c415SKurt Borja }
4798cc2c415SKurt Borja
4808cc2c415SKurt Borja pr_err("alienware-wmi: unknown amplifier cable status: %d\n", ret);
4818cc2c415SKurt Borja return sysfs_emit(buf, "unconnected connected [unknown]\n");
4828cc2c415SKurt Borja }
4838cc2c415SKurt Borja
4848cc2c415SKurt Borja static DEVICE_ATTR_RO(status);
4858cc2c415SKurt Borja
amplifier_group_visible(struct kobject * kobj)4868cc2c415SKurt Borja static bool amplifier_group_visible(struct kobject *kobj)
4878cc2c415SKurt Borja {
4888cc2c415SKurt Borja return alienware_interface == WMAX && alienfx->amplifier;
4898cc2c415SKurt Borja }
4908cc2c415SKurt Borja DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
4918cc2c415SKurt Borja
4928cc2c415SKurt Borja static struct attribute *amplifier_attrs[] = {
4938cc2c415SKurt Borja &dev_attr_status.attr,
4948cc2c415SKurt Borja NULL,
4958cc2c415SKurt Borja };
4968cc2c415SKurt Borja
4978cc2c415SKurt Borja const struct attribute_group wmax_amplifier_attribute_group = {
4988cc2c415SKurt Borja .name = "amplifier",
4998cc2c415SKurt Borja .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
5008cc2c415SKurt Borja .attrs = amplifier_attrs,
5018cc2c415SKurt Borja };
5028cc2c415SKurt Borja
5038cc2c415SKurt Borja /*
5048cc2c415SKurt Borja * Deep Sleep Control support
5058cc2c415SKurt Borja * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
5068cc2c415SKurt Borja */
deepsleep_show(struct device * dev,struct device_attribute * attr,char * buf)5078cc2c415SKurt Borja static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr,
5088cc2c415SKurt Borja char *buf)
5098cc2c415SKurt Borja {
5108cc2c415SKurt Borja struct alienfx_platdata *pdata = dev_get_platdata(dev);
5118cc2c415SKurt Borja struct wmax_basic_args in_args = {
5128cc2c415SKurt Borja .arg = 0,
5138cc2c415SKurt Borja };
5148cc2c415SKurt Borja u32 out_data;
5158cc2c415SKurt Borja int ret;
5168cc2c415SKurt Borja
5178cc2c415SKurt Borja ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
5188cc2c415SKurt Borja &in_args, sizeof(in_args), &out_data);
5198cc2c415SKurt Borja if (!ret) {
5208cc2c415SKurt Borja if (out_data == 0)
5218cc2c415SKurt Borja return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
5228cc2c415SKurt Borja else if (out_data == 1)
5238cc2c415SKurt Borja return sysfs_emit(buf, "disabled [s5] s5_s4\n");
5248cc2c415SKurt Borja else if (out_data == 2)
5258cc2c415SKurt Borja return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
5268cc2c415SKurt Borja }
5278cc2c415SKurt Borja
5288cc2c415SKurt Borja pr_err("alienware-wmi: unknown deep sleep status: %d\n", ret);
5298cc2c415SKurt Borja return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
5308cc2c415SKurt Borja }
5318cc2c415SKurt Borja
deepsleep_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)5328cc2c415SKurt Borja static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr,
5338cc2c415SKurt Borja const char *buf, size_t count)
5348cc2c415SKurt Borja {
5358cc2c415SKurt Borja struct alienfx_platdata *pdata = dev_get_platdata(dev);
5368cc2c415SKurt Borja struct wmax_basic_args args;
5378cc2c415SKurt Borja int ret;
5388cc2c415SKurt Borja
5398cc2c415SKurt Borja if (strcmp(buf, "disabled\n") == 0)
5408cc2c415SKurt Borja args.arg = 0;
5418cc2c415SKurt Borja else if (strcmp(buf, "s5\n") == 0)
5428cc2c415SKurt Borja args.arg = 1;
5438cc2c415SKurt Borja else
5448cc2c415SKurt Borja args.arg = 2;
5458cc2c415SKurt Borja pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
5468cc2c415SKurt Borja
5478cc2c415SKurt Borja ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
5488cc2c415SKurt Borja &args, sizeof(args), NULL);
5498cc2c415SKurt Borja if (!ret)
5508cc2c415SKurt Borja pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret);
5518cc2c415SKurt Borja
5528cc2c415SKurt Borja return count;
5538cc2c415SKurt Borja }
5548cc2c415SKurt Borja
5558cc2c415SKurt Borja static DEVICE_ATTR_RW(deepsleep);
5568cc2c415SKurt Borja
deepsleep_group_visible(struct kobject * kobj)5578cc2c415SKurt Borja static bool deepsleep_group_visible(struct kobject *kobj)
5588cc2c415SKurt Borja {
5598cc2c415SKurt Borja return alienware_interface == WMAX && alienfx->deepslp;
5608cc2c415SKurt Borja }
5618cc2c415SKurt Borja DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
5628cc2c415SKurt Borja
5638cc2c415SKurt Borja static struct attribute *deepsleep_attrs[] = {
5648cc2c415SKurt Borja &dev_attr_deepsleep.attr,
5658cc2c415SKurt Borja NULL,
5668cc2c415SKurt Borja };
5678cc2c415SKurt Borja
5688cc2c415SKurt Borja const struct attribute_group wmax_deepsleep_attribute_group = {
5698cc2c415SKurt Borja .name = "deepsleep",
5708cc2c415SKurt Borja .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
5718cc2c415SKurt Borja .attrs = deepsleep_attrs,
5728cc2c415SKurt Borja };
5738cc2c415SKurt Borja
5748cc2c415SKurt Borja /*
57545983d19SKurt Borja * AWCC Helpers
5768cc2c415SKurt Borja */
is_awcc_thermal_profile_id(u8 code)577a000da9dSKurt Borja static bool is_awcc_thermal_profile_id(u8 code)
5788cc2c415SKurt Borja {
579a000da9dSKurt Borja u8 table = FIELD_GET(AWCC_THERMAL_TABLE_MASK, code);
580a000da9dSKurt Borja u8 mode = FIELD_GET(AWCC_THERMAL_MODE_MASK, code);
581a000da9dSKurt Borja
582a000da9dSKurt Borja if (mode >= AWCC_PROFILE_LAST)
5838cc2c415SKurt Borja return false;
5848cc2c415SKurt Borja
585a000da9dSKurt Borja if (table == AWCC_THERMAL_TABLE_LEGACY && mode >= AWCC_PROFILE_LEGACY_QUIET)
5868cc2c415SKurt Borja return true;
5878cc2c415SKurt Borja
588a000da9dSKurt Borja if (table == AWCC_THERMAL_TABLE_USTT && mode <= AWCC_PROFILE_USTT_LOW_POWER)
5898cc2c415SKurt Borja return true;
5908cc2c415SKurt Borja
5918cc2c415SKurt Borja return false;
5928cc2c415SKurt Borja }
5938cc2c415SKurt Borja
awcc_wmi_command(struct wmi_device * wdev,u32 method_id,struct wmax_u32_args * args,u32 * out)59445983d19SKurt Borja static int awcc_wmi_command(struct wmi_device *wdev, u32 method_id,
59545983d19SKurt Borja struct wmax_u32_args *args, u32 *out)
5968cc2c415SKurt Borja {
5978cc2c415SKurt Borja int ret;
5988cc2c415SKurt Borja
59945983d19SKurt Borja ret = alienware_wmi_command(wdev, method_id, args, sizeof(*args), out);
60045983d19SKurt Borja if (ret)
6018cc2c415SKurt Borja return ret;
6028cc2c415SKurt Borja
60345983d19SKurt Borja if (*out == AWCC_FAILURE_CODE || *out == AWCC_FAILURE_CODE_2)
6048cc2c415SKurt Borja return -EBADRQC;
6058cc2c415SKurt Borja
6068cc2c415SKurt Borja return 0;
6078cc2c415SKurt Borja }
6088cc2c415SKurt Borja
awcc_get_fan_sensors(struct wmi_device * wdev,u8 operation,u8 fan_id,u8 index,u32 * out)609d6999078SKurt Borja static int awcc_get_fan_sensors(struct wmi_device *wdev, u8 operation,
610d6999078SKurt Borja u8 fan_id, u8 index, u32 *out)
611d6999078SKurt Borja {
612d6999078SKurt Borja struct wmax_u32_args args = {
613d6999078SKurt Borja .operation = operation,
614d6999078SKurt Borja .arg1 = fan_id,
615d6999078SKurt Borja .arg2 = index,
616d6999078SKurt Borja .arg3 = 0,
617d6999078SKurt Borja };
618d6999078SKurt Borja
619d6999078SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_GET_FAN_SENSORS, &args, out);
620d6999078SKurt Borja }
621d6999078SKurt Borja
awcc_thermal_information(struct wmi_device * wdev,u8 operation,u8 arg,u32 * out)62245983d19SKurt Borja static int awcc_thermal_information(struct wmi_device *wdev, u8 operation, u8 arg,
62345983d19SKurt Borja u32 *out)
6248cc2c415SKurt Borja {
62545983d19SKurt Borja struct wmax_u32_args args = {
62645983d19SKurt Borja .operation = operation,
62745983d19SKurt Borja .arg1 = arg,
62845983d19SKurt Borja .arg2 = 0,
62945983d19SKurt Borja .arg3 = 0,
63045983d19SKurt Borja };
63145983d19SKurt Borja
63245983d19SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
63345983d19SKurt Borja }
63445983d19SKurt Borja
awcc_fwup_gpio_control(struct wmi_device * wdev,u8 pin,u8 status)635aee5cf93SKurt Borja static int awcc_fwup_gpio_control(struct wmi_device *wdev, u8 pin, u8 status)
636aee5cf93SKurt Borja {
637aee5cf93SKurt Borja struct wmax_u32_args args = {
638aee5cf93SKurt Borja .operation = pin,
639aee5cf93SKurt Borja .arg1 = status,
640aee5cf93SKurt Borja .arg2 = 0,
641aee5cf93SKurt Borja .arg3 = 0,
642aee5cf93SKurt Borja };
643aee5cf93SKurt Borja u32 out;
644aee5cf93SKurt Borja
645aee5cf93SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_FWUP_GPIO_CONTROL, &args, &out);
646aee5cf93SKurt Borja }
647aee5cf93SKurt Borja
awcc_read_total_gpios(struct wmi_device * wdev,u32 * count)648aee5cf93SKurt Borja static int awcc_read_total_gpios(struct wmi_device *wdev, u32 *count)
649aee5cf93SKurt Borja {
650aee5cf93SKurt Borja struct wmax_u32_args args = {};
651aee5cf93SKurt Borja
652aee5cf93SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_READ_TOTAL_GPIOS, &args, count);
653aee5cf93SKurt Borja }
654aee5cf93SKurt Borja
awcc_read_gpio_status(struct wmi_device * wdev,u8 pin,u32 * status)655aee5cf93SKurt Borja static int awcc_read_gpio_status(struct wmi_device *wdev, u8 pin, u32 *status)
656aee5cf93SKurt Borja {
657aee5cf93SKurt Borja struct wmax_u32_args args = {
658aee5cf93SKurt Borja .operation = pin,
659aee5cf93SKurt Borja .arg1 = 0,
660aee5cf93SKurt Borja .arg2 = 0,
661aee5cf93SKurt Borja .arg3 = 0,
662aee5cf93SKurt Borja };
663aee5cf93SKurt Borja
664aee5cf93SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_READ_GPIO_STATUS, &args, status);
665aee5cf93SKurt Borja }
666aee5cf93SKurt Borja
awcc_game_shift_status(struct wmi_device * wdev,u8 operation,u32 * out)66745983d19SKurt Borja static int awcc_game_shift_status(struct wmi_device *wdev, u8 operation,
66845983d19SKurt Borja u32 *out)
66945983d19SKurt Borja {
67045983d19SKurt Borja struct wmax_u32_args args = {
67145983d19SKurt Borja .operation = operation,
67245983d19SKurt Borja .arg1 = 0,
67345983d19SKurt Borja .arg2 = 0,
67445983d19SKurt Borja .arg3 = 0,
67545983d19SKurt Borja };
67645983d19SKurt Borja
67745983d19SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_GAME_SHIFT_STATUS, &args, out);
67845983d19SKurt Borja }
67945983d19SKurt Borja
68045983d19SKurt Borja /**
68145983d19SKurt Borja * awcc_op_get_resource_id - Get the resource ID at a given index
68245983d19SKurt Borja * @wdev: AWCC WMI device
68345983d19SKurt Borja * @index: Index
68445983d19SKurt Borja * @out: Value returned by the WMI call
68545983d19SKurt Borja *
68645983d19SKurt Borja * Get the resource ID at a given @index. Resource IDs are listed in the
68745983d19SKurt Borja * following order:
68845983d19SKurt Borja *
68945983d19SKurt Borja * - Fan IDs
69045983d19SKurt Borja * - Sensor IDs
69145983d19SKurt Borja * - Unknown IDs
69245983d19SKurt Borja * - Thermal Profile IDs
69345983d19SKurt Borja *
69445983d19SKurt Borja * The total number of IDs of a given type can be obtained with
69545983d19SKurt Borja * AWCC_OP_GET_SYSTEM_DESCRIPTION.
69645983d19SKurt Borja *
69745983d19SKurt Borja * Return: 0 on success, -errno on failure
69845983d19SKurt Borja */
awcc_op_get_resource_id(struct wmi_device * wdev,u8 index,u8 * out)69945983d19SKurt Borja static int awcc_op_get_resource_id(struct wmi_device *wdev, u8 index, u8 *out)
70045983d19SKurt Borja {
70145983d19SKurt Borja struct wmax_u32_args args = {
70245983d19SKurt Borja .operation = AWCC_OP_GET_RESOURCE_ID,
70345983d19SKurt Borja .arg1 = index,
7048cc2c415SKurt Borja .arg2 = 0,
7058cc2c415SKurt Borja .arg3 = 0,
7068cc2c415SKurt Borja };
7078cc2c415SKurt Borja u32 out_data;
7088cc2c415SKurt Borja int ret;
7098cc2c415SKurt Borja
71045983d19SKurt Borja ret = awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, &out_data);
7118cc2c415SKurt Borja if (ret)
7128cc2c415SKurt Borja return ret;
7138cc2c415SKurt Borja
71445983d19SKurt Borja *out = FIELD_GET(AWCC_RESOURCE_ID_MASK, out_data);
7158cc2c415SKurt Borja
7168cc2c415SKurt Borja return 0;
7178cc2c415SKurt Borja }
7188cc2c415SKurt Borja
awcc_op_get_fan_rpm(struct wmi_device * wdev,u8 fan_id,u32 * out)719d6999078SKurt Borja static int awcc_op_get_fan_rpm(struct wmi_device *wdev, u8 fan_id, u32 *out)
720d6999078SKurt Borja {
721d6999078SKurt Borja struct wmax_u32_args args = {
722d6999078SKurt Borja .operation = AWCC_OP_GET_FAN_RPM,
723d6999078SKurt Borja .arg1 = fan_id,
724d6999078SKurt Borja .arg2 = 0,
725d6999078SKurt Borja .arg3 = 0,
726d6999078SKurt Borja };
727d6999078SKurt Borja
728d6999078SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
729d6999078SKurt Borja }
730d6999078SKurt Borja
awcc_op_get_temperature(struct wmi_device * wdev,u8 temp_id,u32 * out)731d6999078SKurt Borja static int awcc_op_get_temperature(struct wmi_device *wdev, u8 temp_id, u32 *out)
732d6999078SKurt Borja {
733d6999078SKurt Borja struct wmax_u32_args args = {
734d6999078SKurt Borja .operation = AWCC_OP_GET_TEMPERATURE,
735d6999078SKurt Borja .arg1 = temp_id,
736d6999078SKurt Borja .arg2 = 0,
737d6999078SKurt Borja .arg3 = 0,
738d6999078SKurt Borja };
739d6999078SKurt Borja
740d6999078SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
741d6999078SKurt Borja }
742d6999078SKurt Borja
awcc_op_get_fan_boost(struct wmi_device * wdev,u8 fan_id,u32 * out)74307ac2759SKurt Borja static int awcc_op_get_fan_boost(struct wmi_device *wdev, u8 fan_id, u32 *out)
74407ac2759SKurt Borja {
74507ac2759SKurt Borja struct wmax_u32_args args = {
74607ac2759SKurt Borja .operation = AWCC_OP_GET_FAN_BOOST,
74707ac2759SKurt Borja .arg1 = fan_id,
74807ac2759SKurt Borja .arg2 = 0,
74907ac2759SKurt Borja .arg3 = 0,
75007ac2759SKurt Borja };
75107ac2759SKurt Borja
75207ac2759SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
75307ac2759SKurt Borja }
75407ac2759SKurt Borja
awcc_op_get_current_profile(struct wmi_device * wdev,u32 * out)75545983d19SKurt Borja static int awcc_op_get_current_profile(struct wmi_device *wdev, u32 *out)
7568cc2c415SKurt Borja {
75745983d19SKurt Borja struct wmax_u32_args args = {
75845983d19SKurt Borja .operation = AWCC_OP_GET_CURRENT_PROFILE,
7598cc2c415SKurt Borja .arg1 = 0,
7608cc2c415SKurt Borja .arg2 = 0,
7618cc2c415SKurt Borja .arg3 = 0,
7628cc2c415SKurt Borja };
7638cc2c415SKurt Borja
76445983d19SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
7658cc2c415SKurt Borja }
7668cc2c415SKurt Borja
awcc_op_activate_profile(struct wmi_device * wdev,u8 profile)76745983d19SKurt Borja static int awcc_op_activate_profile(struct wmi_device *wdev, u8 profile)
76845983d19SKurt Borja {
76945983d19SKurt Borja struct wmax_u32_args args = {
77045983d19SKurt Borja .operation = AWCC_OP_ACTIVATE_PROFILE,
77145983d19SKurt Borja .arg1 = profile,
77245983d19SKurt Borja .arg2 = 0,
77345983d19SKurt Borja .arg3 = 0,
77445983d19SKurt Borja };
77545983d19SKurt Borja u32 out;
77645983d19SKurt Borja
77745983d19SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
77845983d19SKurt Borja }
77945983d19SKurt Borja
awcc_op_set_fan_boost(struct wmi_device * wdev,u8 fan_id,u8 boost)78007ac2759SKurt Borja static int awcc_op_set_fan_boost(struct wmi_device *wdev, u8 fan_id, u8 boost)
78107ac2759SKurt Borja {
78207ac2759SKurt Borja struct wmax_u32_args args = {
78307ac2759SKurt Borja .operation = AWCC_OP_SET_FAN_BOOST,
78407ac2759SKurt Borja .arg1 = fan_id,
78507ac2759SKurt Borja .arg2 = boost,
78607ac2759SKurt Borja .arg3 = 0,
78707ac2759SKurt Borja };
78807ac2759SKurt Borja u32 out;
78907ac2759SKurt Borja
79007ac2759SKurt Borja return awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
79107ac2759SKurt Borja }
79207ac2759SKurt Borja
79345983d19SKurt Borja /*
794d6999078SKurt Borja * HWMON
795d6999078SKurt Borja * - Provides temperature and fan speed monitoring as well as manual fan
796d6999078SKurt Borja * control
797d6999078SKurt Borja */
awcc_hwmon_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int channel)798d6999078SKurt Borja static umode_t awcc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
799d6999078SKurt Borja u32 attr, int channel)
800d6999078SKurt Borja {
801d6999078SKurt Borja const struct awcc_priv *priv = drvdata;
802d6999078SKurt Borja unsigned int temp_count;
803d6999078SKurt Borja
804d6999078SKurt Borja switch (type) {
805d6999078SKurt Borja case hwmon_temp:
806d6999078SKurt Borja temp_count = bitmap_weight(priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
807d6999078SKurt Borja
808d6999078SKurt Borja return channel < temp_count ? 0444 : 0;
809d6999078SKurt Borja case hwmon_fan:
810d6999078SKurt Borja return channel < priv->fan_count ? 0444 : 0;
811d6999078SKurt Borja case hwmon_pwm:
812d6999078SKurt Borja return channel < priv->fan_count ? 0444 : 0;
813d6999078SKurt Borja default:
814d6999078SKurt Borja return 0;
815d6999078SKurt Borja }
816d6999078SKurt Borja }
817d6999078SKurt Borja
awcc_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)818d6999078SKurt Borja static int awcc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
819d6999078SKurt Borja u32 attr, int channel, long *val)
820d6999078SKurt Borja {
821d6999078SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
822d6999078SKurt Borja const struct awcc_fan_data *fan;
823d6999078SKurt Borja u32 state;
824d6999078SKurt Borja int ret;
825d6999078SKurt Borja u8 temp;
826d6999078SKurt Borja
827d6999078SKurt Borja switch (type) {
828d6999078SKurt Borja case hwmon_temp:
829d6999078SKurt Borja temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel);
830d6999078SKurt Borja
831d6999078SKurt Borja switch (attr) {
832d6999078SKurt Borja case hwmon_temp_input:
833d6999078SKurt Borja ret = awcc_op_get_temperature(priv->wdev, temp, &state);
834d6999078SKurt Borja if (ret)
835d6999078SKurt Borja return ret;
836d6999078SKurt Borja
837d6999078SKurt Borja *val = state * MILLIDEGREE_PER_DEGREE;
838d6999078SKurt Borja break;
839d6999078SKurt Borja default:
840d6999078SKurt Borja return -EOPNOTSUPP;
841d6999078SKurt Borja }
842d6999078SKurt Borja
843d6999078SKurt Borja break;
844d6999078SKurt Borja case hwmon_fan:
845d6999078SKurt Borja fan = priv->fan_data[channel];
846d6999078SKurt Borja
847d6999078SKurt Borja switch (attr) {
848d6999078SKurt Borja case hwmon_fan_input:
849d6999078SKurt Borja ret = awcc_op_get_fan_rpm(priv->wdev, fan->id, &state);
850d6999078SKurt Borja if (ret)
851d6999078SKurt Borja return ret;
852d6999078SKurt Borja
853d6999078SKurt Borja *val = state;
854d6999078SKurt Borja break;
855d6999078SKurt Borja case hwmon_fan_min:
856d6999078SKurt Borja *val = fan->min_rpm;
857d6999078SKurt Borja break;
858d6999078SKurt Borja case hwmon_fan_max:
859d6999078SKurt Borja *val = fan->max_rpm;
860d6999078SKurt Borja break;
861d6999078SKurt Borja default:
862d6999078SKurt Borja return -EOPNOTSUPP;
863d6999078SKurt Borja }
864d6999078SKurt Borja
865d6999078SKurt Borja break;
866d6999078SKurt Borja case hwmon_pwm:
867d6999078SKurt Borja fan = priv->fan_data[channel];
868d6999078SKurt Borja
869d6999078SKurt Borja switch (attr) {
870d6999078SKurt Borja case hwmon_pwm_auto_channels_temp:
871d6999078SKurt Borja *val = fan->auto_channels_temp;
872d6999078SKurt Borja break;
873d6999078SKurt Borja default:
874d6999078SKurt Borja return -EOPNOTSUPP;
875d6999078SKurt Borja }
876d6999078SKurt Borja
877d6999078SKurt Borja break;
878d6999078SKurt Borja default:
879d6999078SKurt Borja return -EOPNOTSUPP;
880d6999078SKurt Borja }
881d6999078SKurt Borja
882d6999078SKurt Borja return 0;
883d6999078SKurt Borja }
884d6999078SKurt Borja
awcc_hwmon_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)885d6999078SKurt Borja static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
886d6999078SKurt Borja u32 attr, int channel, const char **str)
887d6999078SKurt Borja {
888d6999078SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
889d6999078SKurt Borja u8 temp;
890d6999078SKurt Borja
891d6999078SKurt Borja switch (type) {
892d6999078SKurt Borja case hwmon_temp:
893d6999078SKurt Borja temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel);
894d6999078SKurt Borja
895d6999078SKurt Borja switch (temp) {
896d6999078SKurt Borja case AWCC_TEMP_SENSOR_CPU:
897d6999078SKurt Borja *str = "CPU";
898d6999078SKurt Borja break;
899d6999078SKurt Borja case AWCC_TEMP_SENSOR_GPU:
900d6999078SKurt Borja *str = "GPU";
901d6999078SKurt Borja break;
902d6999078SKurt Borja default:
903d6999078SKurt Borja *str = "Unknown";
904d6999078SKurt Borja break;
905d6999078SKurt Borja }
906d6999078SKurt Borja
907d6999078SKurt Borja break;
908d6999078SKurt Borja case hwmon_fan:
909d6999078SKurt Borja *str = priv->fan_data[channel]->label;
910d6999078SKurt Borja break;
911d6999078SKurt Borja default:
912d6999078SKurt Borja return -EOPNOTSUPP;
913d6999078SKurt Borja }
914d6999078SKurt Borja
915d6999078SKurt Borja return 0;
916d6999078SKurt Borja }
917d6999078SKurt Borja
918d6999078SKurt Borja static const struct hwmon_ops awcc_hwmon_ops = {
919d6999078SKurt Borja .is_visible = awcc_hwmon_is_visible,
920d6999078SKurt Borja .read = awcc_hwmon_read,
921d6999078SKurt Borja .read_string = awcc_hwmon_read_string,
922d6999078SKurt Borja };
923d6999078SKurt Borja
924d6999078SKurt Borja static const struct hwmon_channel_info * const awcc_hwmon_info[] = {
925d6999078SKurt Borja HWMON_CHANNEL_INFO(temp,
926d6999078SKurt Borja HWMON_T_LABEL | HWMON_T_INPUT,
927d6999078SKurt Borja HWMON_T_LABEL | HWMON_T_INPUT,
928d6999078SKurt Borja HWMON_T_LABEL | HWMON_T_INPUT,
929d6999078SKurt Borja HWMON_T_LABEL | HWMON_T_INPUT,
930d6999078SKurt Borja HWMON_T_LABEL | HWMON_T_INPUT,
931d6999078SKurt Borja HWMON_T_LABEL | HWMON_T_INPUT
932d6999078SKurt Borja ),
933d6999078SKurt Borja HWMON_CHANNEL_INFO(fan,
934d6999078SKurt Borja HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
935d6999078SKurt Borja HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
936d6999078SKurt Borja HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
937d6999078SKurt Borja HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
938d6999078SKurt Borja HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
939d6999078SKurt Borja HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX
940d6999078SKurt Borja ),
941d6999078SKurt Borja HWMON_CHANNEL_INFO(pwm,
942d6999078SKurt Borja HWMON_PWM_AUTO_CHANNELS_TEMP,
943d6999078SKurt Borja HWMON_PWM_AUTO_CHANNELS_TEMP,
944d6999078SKurt Borja HWMON_PWM_AUTO_CHANNELS_TEMP,
945d6999078SKurt Borja HWMON_PWM_AUTO_CHANNELS_TEMP,
946d6999078SKurt Borja HWMON_PWM_AUTO_CHANNELS_TEMP,
947d6999078SKurt Borja HWMON_PWM_AUTO_CHANNELS_TEMP
948d6999078SKurt Borja ),
949d6999078SKurt Borja NULL
950d6999078SKurt Borja };
951d6999078SKurt Borja
952d6999078SKurt Borja static const struct hwmon_chip_info awcc_hwmon_chip_info = {
953d6999078SKurt Borja .ops = &awcc_hwmon_ops,
954d6999078SKurt Borja .info = awcc_hwmon_info,
955d6999078SKurt Borja };
956d6999078SKurt Borja
fan_boost_show(struct device * dev,struct device_attribute * attr,char * buf)95707ac2759SKurt Borja static ssize_t fan_boost_show(struct device *dev, struct device_attribute *attr,
95807ac2759SKurt Borja char *buf)
95907ac2759SKurt Borja {
96007ac2759SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
96107ac2759SKurt Borja int index = to_sensor_dev_attr(attr)->index;
96207ac2759SKurt Borja struct awcc_fan_data *fan = priv->fan_data[index];
96307ac2759SKurt Borja u32 boost;
96407ac2759SKurt Borja int ret;
96507ac2759SKurt Borja
96607ac2759SKurt Borja ret = awcc_op_get_fan_boost(priv->wdev, fan->id, &boost);
96707ac2759SKurt Borja if (ret)
96807ac2759SKurt Borja return ret;
96907ac2759SKurt Borja
97007ac2759SKurt Borja return sysfs_emit(buf, "%u\n", boost);
97107ac2759SKurt Borja }
97207ac2759SKurt Borja
fan_boost_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)97307ac2759SKurt Borja static ssize_t fan_boost_store(struct device *dev, struct device_attribute *attr,
97407ac2759SKurt Borja const char *buf, size_t count)
97507ac2759SKurt Borja {
97607ac2759SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
97707ac2759SKurt Borja int index = to_sensor_dev_attr(attr)->index;
97807ac2759SKurt Borja struct awcc_fan_data *fan = priv->fan_data[index];
97907ac2759SKurt Borja unsigned long val;
98007ac2759SKurt Borja int ret;
98107ac2759SKurt Borja
98207ac2759SKurt Borja ret = kstrtoul(buf, 0, &val);
98307ac2759SKurt Borja if (ret)
98407ac2759SKurt Borja return ret;
98507ac2759SKurt Borja
98607ac2759SKurt Borja ret = awcc_op_set_fan_boost(priv->wdev, fan->id, clamp_val(val, 0, 255));
98707ac2759SKurt Borja
98807ac2759SKurt Borja return ret ? ret : count;
98907ac2759SKurt Borja }
99007ac2759SKurt Borja
99107ac2759SKurt Borja static SENSOR_DEVICE_ATTR_RW(fan1_boost, fan_boost, 0);
99207ac2759SKurt Borja static SENSOR_DEVICE_ATTR_RW(fan2_boost, fan_boost, 1);
99307ac2759SKurt Borja static SENSOR_DEVICE_ATTR_RW(fan3_boost, fan_boost, 2);
99407ac2759SKurt Borja static SENSOR_DEVICE_ATTR_RW(fan4_boost, fan_boost, 3);
99507ac2759SKurt Borja static SENSOR_DEVICE_ATTR_RW(fan5_boost, fan_boost, 4);
99607ac2759SKurt Borja static SENSOR_DEVICE_ATTR_RW(fan6_boost, fan_boost, 5);
99707ac2759SKurt Borja
fan_boost_attr_visible(struct kobject * kobj,struct attribute * attr,int n)99807ac2759SKurt Borja static umode_t fan_boost_attr_visible(struct kobject *kobj, struct attribute *attr, int n)
99907ac2759SKurt Borja {
100007ac2759SKurt Borja struct awcc_priv *priv = dev_get_drvdata(kobj_to_dev(kobj));
100107ac2759SKurt Borja
100207ac2759SKurt Borja return n < priv->fan_count ? attr->mode : 0;
100307ac2759SKurt Borja }
100407ac2759SKurt Borja
fan_boost_group_visible(struct kobject * kobj)100507ac2759SKurt Borja static bool fan_boost_group_visible(struct kobject *kobj)
100607ac2759SKurt Borja {
100707ac2759SKurt Borja return true;
100807ac2759SKurt Borja }
100907ac2759SKurt Borja
101007ac2759SKurt Borja DEFINE_SYSFS_GROUP_VISIBLE(fan_boost);
101107ac2759SKurt Borja
101207ac2759SKurt Borja static struct attribute *fan_boost_attrs[] = {
101307ac2759SKurt Borja &sensor_dev_attr_fan1_boost.dev_attr.attr,
101407ac2759SKurt Borja &sensor_dev_attr_fan2_boost.dev_attr.attr,
101507ac2759SKurt Borja &sensor_dev_attr_fan3_boost.dev_attr.attr,
101607ac2759SKurt Borja &sensor_dev_attr_fan4_boost.dev_attr.attr,
101707ac2759SKurt Borja &sensor_dev_attr_fan5_boost.dev_attr.attr,
101807ac2759SKurt Borja &sensor_dev_attr_fan6_boost.dev_attr.attr,
101907ac2759SKurt Borja NULL
102007ac2759SKurt Borja };
102107ac2759SKurt Borja
102207ac2759SKurt Borja static const struct attribute_group fan_boost_group = {
102307ac2759SKurt Borja .attrs = fan_boost_attrs,
102407ac2759SKurt Borja .is_visible = SYSFS_GROUP_VISIBLE(fan_boost),
102507ac2759SKurt Borja };
102607ac2759SKurt Borja
102707ac2759SKurt Borja static const struct attribute_group *awcc_hwmon_groups[] = {
102807ac2759SKurt Borja &fan_boost_group,
102907ac2759SKurt Borja NULL
103007ac2759SKurt Borja };
103107ac2759SKurt Borja
awcc_hwmon_temps_init(struct wmi_device * wdev)1032d6999078SKurt Borja static int awcc_hwmon_temps_init(struct wmi_device *wdev)
1033d6999078SKurt Borja {
1034d6999078SKurt Borja struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
1035d6999078SKurt Borja unsigned int i;
1036d6999078SKurt Borja int ret;
1037d6999078SKurt Borja u8 id;
1038d6999078SKurt Borja
1039d6999078SKurt Borja for (i = 0; i < priv->temp_count; i++) {
1040d6999078SKurt Borja /*
1041d6999078SKurt Borja * Temperature sensors IDs are listed after the fan IDs at
1042d6999078SKurt Borja * offset `fan_count`
1043d6999078SKurt Borja */
1044d6999078SKurt Borja ret = awcc_op_get_resource_id(wdev, i + priv->fan_count, &id);
1045d6999078SKurt Borja if (ret)
1046d6999078SKurt Borja return ret;
1047d6999078SKurt Borja
1048d6999078SKurt Borja __set_bit(id, priv->temp_sensors);
1049d6999078SKurt Borja }
1050d6999078SKurt Borja
1051d6999078SKurt Borja return 0;
1052d6999078SKurt Borja }
1053d6999078SKurt Borja
awcc_get_fan_label(unsigned long * fan_temps)10544b4da10bSKurt Borja static char *awcc_get_fan_label(unsigned long *fan_temps)
1055d6999078SKurt Borja {
10564b4da10bSKurt Borja unsigned int temp_count = bitmap_weight(fan_temps, AWCC_ID_BITMAP_SIZE);
1057d6999078SKurt Borja char *label;
10584b4da10bSKurt Borja u8 temp_id;
1059d6999078SKurt Borja
1060d6999078SKurt Borja switch (temp_count) {
1061d6999078SKurt Borja case 0:
1062d6999078SKurt Borja label = "Independent Fan";
1063d6999078SKurt Borja break;
1064d6999078SKurt Borja case 1:
10654b4da10bSKurt Borja temp_id = find_first_bit(fan_temps, AWCC_ID_BITMAP_SIZE);
10664b4da10bSKurt Borja
1067d6999078SKurt Borja switch (temp_id) {
1068d6999078SKurt Borja case AWCC_TEMP_SENSOR_CPU:
1069d6999078SKurt Borja label = "Processor Fan";
1070d6999078SKurt Borja break;
1071d6999078SKurt Borja case AWCC_TEMP_SENSOR_GPU:
1072d6999078SKurt Borja label = "Video Fan";
1073d6999078SKurt Borja break;
1074d6999078SKurt Borja default:
1075d6999078SKurt Borja label = "Unknown Fan";
1076d6999078SKurt Borja break;
1077d6999078SKurt Borja }
1078d6999078SKurt Borja
1079d6999078SKurt Borja break;
1080d6999078SKurt Borja default:
1081d6999078SKurt Borja label = "Shared Fan";
1082d6999078SKurt Borja break;
1083d6999078SKurt Borja }
1084d6999078SKurt Borja
1085d6999078SKurt Borja return label;
1086d6999078SKurt Borja }
1087d6999078SKurt Borja
awcc_hwmon_fans_init(struct wmi_device * wdev)1088d6999078SKurt Borja static int awcc_hwmon_fans_init(struct wmi_device *wdev)
1089d6999078SKurt Borja {
1090d6999078SKurt Borja struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
1091d6999078SKurt Borja unsigned long fan_temps[AWCC_ID_BITMAP_LONGS];
1092d6999078SKurt Borja unsigned long gather[AWCC_ID_BITMAP_LONGS];
1093d6999078SKurt Borja u32 min_rpm, max_rpm, temp_count, temp_id;
1094d6999078SKurt Borja struct awcc_fan_data *fan_data;
1095d6999078SKurt Borja unsigned int i, j;
1096d6999078SKurt Borja int ret;
1097d6999078SKurt Borja u8 id;
1098d6999078SKurt Borja
1099d6999078SKurt Borja for (i = 0; i < priv->fan_count; i++) {
1100d6999078SKurt Borja fan_data = devm_kzalloc(&wdev->dev, sizeof(*fan_data), GFP_KERNEL);
1101d6999078SKurt Borja if (!fan_data)
1102d6999078SKurt Borja return -ENOMEM;
1103d6999078SKurt Borja
1104d6999078SKurt Borja /*
1105d6999078SKurt Borja * Fan IDs are listed first at offset 0
1106d6999078SKurt Borja */
1107d6999078SKurt Borja ret = awcc_op_get_resource_id(wdev, i, &id);
1108d6999078SKurt Borja if (ret)
1109d6999078SKurt Borja return ret;
1110d6999078SKurt Borja
1111d6999078SKurt Borja ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MIN_RPM, id,
1112d6999078SKurt Borja &min_rpm);
1113d6999078SKurt Borja if (ret)
1114d6999078SKurt Borja return ret;
1115d6999078SKurt Borja
1116d6999078SKurt Borja ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MAX_RPM, id,
1117d6999078SKurt Borja &max_rpm);
1118d6999078SKurt Borja if (ret)
1119d6999078SKurt Borja return ret;
1120d6999078SKurt Borja
1121d6999078SKurt Borja ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_TOTAL_FAN_TEMPS, id,
1122d6999078SKurt Borja 0, &temp_count);
1123d6999078SKurt Borja if (ret)
1124d6999078SKurt Borja return ret;
1125d6999078SKurt Borja
11261fe9596aSKurt Borja bitmap_zero(fan_temps, AWCC_ID_BITMAP_SIZE);
11271fe9596aSKurt Borja
1128d6999078SKurt Borja for (j = 0; j < temp_count; j++) {
1129d6999078SKurt Borja ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_FAN_TEMP_ID,
1130d6999078SKurt Borja id, j, &temp_id);
1131d6999078SKurt Borja if (ret)
1132d6999078SKurt Borja break;
1133d6999078SKurt Borja
1134d6999078SKurt Borja temp_id = FIELD_GET(AWCC_RESOURCE_ID_MASK, temp_id);
1135d6999078SKurt Borja __set_bit(temp_id, fan_temps);
1136d6999078SKurt Borja }
1137d6999078SKurt Borja
1138d6999078SKurt Borja fan_data->id = id;
1139d6999078SKurt Borja fan_data->min_rpm = min_rpm;
1140d6999078SKurt Borja fan_data->max_rpm = max_rpm;
11414b4da10bSKurt Borja fan_data->label = awcc_get_fan_label(fan_temps);
1142d6999078SKurt Borja bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
1143d6999078SKurt Borja bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG);
1144d6999078SKurt Borja priv->fan_data[i] = fan_data;
1145d6999078SKurt Borja }
1146d6999078SKurt Borja
1147d6999078SKurt Borja return 0;
1148d6999078SKurt Borja }
1149d6999078SKurt Borja
awcc_hwmon_init(struct wmi_device * wdev)1150d6999078SKurt Borja static int awcc_hwmon_init(struct wmi_device *wdev)
1151d6999078SKurt Borja {
1152d6999078SKurt Borja struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
1153d6999078SKurt Borja int ret;
1154d6999078SKurt Borja
1155d6999078SKurt Borja priv->fan_data = devm_kcalloc(&wdev->dev, priv->fan_count,
1156d6999078SKurt Borja sizeof(*priv->fan_data), GFP_KERNEL);
1157d6999078SKurt Borja if (!priv->fan_data)
1158d6999078SKurt Borja return -ENOMEM;
1159d6999078SKurt Borja
1160d6999078SKurt Borja ret = awcc_hwmon_temps_init(wdev);
1161d6999078SKurt Borja if (ret)
1162d6999078SKurt Borja return ret;
1163d6999078SKurt Borja
1164d6999078SKurt Borja ret = awcc_hwmon_fans_init(wdev);
1165d6999078SKurt Borja if (ret)
1166d6999078SKurt Borja return ret;
1167d6999078SKurt Borja
116807ac2759SKurt Borja priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi",
116907ac2759SKurt Borja priv, &awcc_hwmon_chip_info,
117007ac2759SKurt Borja awcc_hwmon_groups);
1171d6999078SKurt Borja
1172d6999078SKurt Borja return PTR_ERR_OR_ZERO(priv->hwdev);
1173d6999078SKurt Borja }
1174d6999078SKurt Borja
awcc_hwmon_suspend(struct device * dev)117507ac2759SKurt Borja static void awcc_hwmon_suspend(struct device *dev)
117607ac2759SKurt Borja {
117707ac2759SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
117807ac2759SKurt Borja struct awcc_fan_data *fan;
117907ac2759SKurt Borja unsigned int i;
118007ac2759SKurt Borja u32 boost;
118107ac2759SKurt Borja int ret;
118207ac2759SKurt Borja
118307ac2759SKurt Borja for (i = 0; i < priv->fan_count; i++) {
118407ac2759SKurt Borja fan = priv->fan_data[i];
118507ac2759SKurt Borja
118607ac2759SKurt Borja ret = awcc_thermal_information(priv->wdev, AWCC_OP_GET_FAN_BOOST,
118707ac2759SKurt Borja fan->id, &boost);
118807ac2759SKurt Borja if (ret)
118907ac2759SKurt Borja dev_err(dev, "Failed to store Fan %u boost while suspending\n", i);
119007ac2759SKurt Borja
119107ac2759SKurt Borja fan->suspend_cache = ret ? 0 : clamp_val(boost, 0, 255);
119207ac2759SKurt Borja
119307ac2759SKurt Borja awcc_op_set_fan_boost(priv->wdev, fan->id, 0);
119407ac2759SKurt Borja if (ret)
119507ac2759SKurt Borja dev_err(dev, "Failed to set Fan %u boost to 0 while suspending\n", i);
119607ac2759SKurt Borja }
119707ac2759SKurt Borja }
119807ac2759SKurt Borja
awcc_hwmon_resume(struct device * dev)119907ac2759SKurt Borja static void awcc_hwmon_resume(struct device *dev)
120007ac2759SKurt Borja {
120107ac2759SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
120207ac2759SKurt Borja struct awcc_fan_data *fan;
120307ac2759SKurt Borja unsigned int i;
120407ac2759SKurt Borja int ret;
120507ac2759SKurt Borja
120607ac2759SKurt Borja for (i = 0; i < priv->fan_count; i++) {
120707ac2759SKurt Borja fan = priv->fan_data[i];
120807ac2759SKurt Borja
120907ac2759SKurt Borja if (!fan->suspend_cache)
121007ac2759SKurt Borja continue;
121107ac2759SKurt Borja
121207ac2759SKurt Borja ret = awcc_op_set_fan_boost(priv->wdev, fan->id, fan->suspend_cache);
121307ac2759SKurt Borja if (ret)
121407ac2759SKurt Borja dev_err(dev, "Failed to restore Fan %u boost while resuming\n", i);
121507ac2759SKurt Borja }
121607ac2759SKurt Borja }
121707ac2759SKurt Borja
1218d6999078SKurt Borja /*
121945983d19SKurt Borja * Thermal Profile control
122045983d19SKurt Borja * - Provides thermal profile control through the Platform Profile API
122145983d19SKurt Borja */
awcc_platform_profile_get(struct device * dev,enum platform_profile_option * profile)12228a1a0fb5SKurt Borja static int awcc_platform_profile_get(struct device *dev,
12238cc2c415SKurt Borja enum platform_profile_option *profile)
12248cc2c415SKurt Borja {
12258cc2c415SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
12268cc2c415SKurt Borja u32 out_data;
12278cc2c415SKurt Borja int ret;
12288cc2c415SKurt Borja
122945983d19SKurt Borja ret = awcc_op_get_current_profile(priv->wdev, &out_data);
123045983d19SKurt Borja if (ret)
12318cc2c415SKurt Borja return ret;
12328cc2c415SKurt Borja
12333dde0ae1SKurt Borja switch (out_data) {
12343dde0ae1SKurt Borja case AWCC_SPECIAL_PROFILE_CUSTOM:
12353dde0ae1SKurt Borja *profile = PLATFORM_PROFILE_CUSTOM;
12363dde0ae1SKurt Borja return 0;
12373dde0ae1SKurt Borja case AWCC_SPECIAL_PROFILE_GMODE:
12388cc2c415SKurt Borja *profile = PLATFORM_PROFILE_PERFORMANCE;
12398cc2c415SKurt Borja return 0;
12403dde0ae1SKurt Borja default:
12413dde0ae1SKurt Borja break;
12428cc2c415SKurt Borja }
12438cc2c415SKurt Borja
1244a000da9dSKurt Borja if (!is_awcc_thermal_profile_id(out_data))
12458cc2c415SKurt Borja return -ENODATA;
12468cc2c415SKurt Borja
1247a000da9dSKurt Borja out_data = FIELD_GET(AWCC_THERMAL_MODE_MASK, out_data);
12488a1a0fb5SKurt Borja *profile = awcc_mode_to_platform_profile[out_data];
12498cc2c415SKurt Borja
12508cc2c415SKurt Borja return 0;
12518cc2c415SKurt Borja }
12528cc2c415SKurt Borja
awcc_platform_profile_set(struct device * dev,enum platform_profile_option profile)12538a1a0fb5SKurt Borja static int awcc_platform_profile_set(struct device *dev,
12548cc2c415SKurt Borja enum platform_profile_option profile)
12558cc2c415SKurt Borja {
12568cc2c415SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
12578cc2c415SKurt Borja
12588cc2c415SKurt Borja if (awcc->gmode) {
12598cc2c415SKurt Borja u32 gmode_status;
12608cc2c415SKurt Borja int ret;
12618cc2c415SKurt Borja
12628a1a0fb5SKurt Borja ret = awcc_game_shift_status(priv->wdev,
12638a1a0fb5SKurt Borja AWCC_OP_GET_GAME_SHIFT_STATUS,
12648cc2c415SKurt Borja &gmode_status);
12658cc2c415SKurt Borja
12668cc2c415SKurt Borja if (ret < 0)
12678cc2c415SKurt Borja return ret;
12688cc2c415SKurt Borja
12698cc2c415SKurt Borja if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
12708cc2c415SKurt Borja (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
12718a1a0fb5SKurt Borja ret = awcc_game_shift_status(priv->wdev,
12728a1a0fb5SKurt Borja AWCC_OP_TOGGLE_GAME_SHIFT,
12738cc2c415SKurt Borja &gmode_status);
12748cc2c415SKurt Borja
12758cc2c415SKurt Borja if (ret < 0)
12768cc2c415SKurt Borja return ret;
12778cc2c415SKurt Borja }
12788cc2c415SKurt Borja }
12798cc2c415SKurt Borja
128077bb2ec5SKurt Borja return awcc_op_activate_profile(priv->wdev, priv->supported_profiles[profile]);
12818cc2c415SKurt Borja }
12828cc2c415SKurt Borja
awcc_platform_profile_probe(void * drvdata,unsigned long * choices)12838a1a0fb5SKurt Borja static int awcc_platform_profile_probe(void *drvdata, unsigned long *choices)
12848cc2c415SKurt Borja {
12858cc2c415SKurt Borja enum platform_profile_option profile;
12868cc2c415SKurt Borja struct awcc_priv *priv = drvdata;
12878a1a0fb5SKurt Borja enum awcc_thermal_profile mode;
128832b6372dSKurt Borja u8 id, offset = 0;
12898cc2c415SKurt Borja int ret;
12908cc2c415SKurt Borja
129132b6372dSKurt Borja /*
129232b6372dSKurt Borja * Thermal profile IDs are listed last at offset
129332b6372dSKurt Borja * fan_count + temp_count + unknown_count
129432b6372dSKurt Borja */
129532b6372dSKurt Borja for (unsigned int i = 0; i < ARRAY_SIZE(priv->res_count) - 1; i++)
129632b6372dSKurt Borja offset += priv->res_count[i];
12978cc2c415SKurt Borja
129832b6372dSKurt Borja for (unsigned int i = 0; i < priv->profile_count; i++) {
129932b6372dSKurt Borja ret = awcc_op_get_resource_id(priv->wdev, i + offset, &id);
130032b6372dSKurt Borja /*
130132b6372dSKurt Borja * Some devices report an incorrect number of thermal profiles
130232b6372dSKurt Borja * so the resource ID list may end prematurely
130332b6372dSKurt Borja */
13048cc2c415SKurt Borja if (ret == -EBADRQC)
13058cc2c415SKurt Borja break;
13064a8e04e2SKurt Borja if (ret)
13074a8e04e2SKurt Borja return ret;
13088cc2c415SKurt Borja
130932b6372dSKurt Borja if (!is_awcc_thermal_profile_id(id)) {
131032b6372dSKurt Borja dev_dbg(&priv->wdev->dev, "Unmapped thermal profile ID 0x%02x\n", id);
13118cc2c415SKurt Borja continue;
131232b6372dSKurt Borja }
13138cc2c415SKurt Borja
1314a000da9dSKurt Borja mode = FIELD_GET(AWCC_THERMAL_MODE_MASK, id);
13158a1a0fb5SKurt Borja profile = awcc_mode_to_platform_profile[mode];
131677bb2ec5SKurt Borja priv->supported_profiles[profile] = id;
13178cc2c415SKurt Borja
131832b6372dSKurt Borja __set_bit(profile, choices);
13198cc2c415SKurt Borja }
13208cc2c415SKurt Borja
13218cc2c415SKurt Borja if (bitmap_empty(choices, PLATFORM_PROFILE_LAST))
13228cc2c415SKurt Borja return -ENODEV;
13238cc2c415SKurt Borja
13248cc2c415SKurt Borja if (awcc->gmode) {
132577bb2ec5SKurt Borja priv->supported_profiles[PLATFORM_PROFILE_PERFORMANCE] =
13263dde0ae1SKurt Borja AWCC_SPECIAL_PROFILE_GMODE;
13278cc2c415SKurt Borja
132832b6372dSKurt Borja __set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
13298cc2c415SKurt Borja }
13308cc2c415SKurt Borja
13313dde0ae1SKurt Borja /* Every model supports the "custom" profile */
13323dde0ae1SKurt Borja priv->supported_profiles[PLATFORM_PROFILE_CUSTOM] =
13333dde0ae1SKurt Borja AWCC_SPECIAL_PROFILE_CUSTOM;
13343dde0ae1SKurt Borja
13353dde0ae1SKurt Borja __set_bit(PLATFORM_PROFILE_CUSTOM, choices);
13363dde0ae1SKurt Borja
13378cc2c415SKurt Borja return 0;
13388cc2c415SKurt Borja }
13398cc2c415SKurt Borja
13408cc2c415SKurt Borja static const struct platform_profile_ops awcc_platform_profile_ops = {
13418a1a0fb5SKurt Borja .probe = awcc_platform_profile_probe,
13428a1a0fb5SKurt Borja .profile_get = awcc_platform_profile_get,
13438a1a0fb5SKurt Borja .profile_set = awcc_platform_profile_set,
13448cc2c415SKurt Borja };
13458cc2c415SKurt Borja
awcc_platform_profile_init(struct wmi_device * wdev)13468cc2c415SKurt Borja static int awcc_platform_profile_init(struct wmi_device *wdev)
13478cc2c415SKurt Borja {
13488cc2c415SKurt Borja struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
13498cc2c415SKurt Borja
13508cc2c415SKurt Borja priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi",
13518cc2c415SKurt Borja priv, &awcc_platform_profile_ops);
13528cc2c415SKurt Borja
13538cc2c415SKurt Borja return PTR_ERR_OR_ZERO(priv->ppdev);
13548cc2c415SKurt Borja }
13558cc2c415SKurt Borja
1356b028fb49SKurt Borja /*
1357b028fb49SKurt Borja * DebugFS
1358b028fb49SKurt Borja */
awcc_debugfs_system_description_read(struct seq_file * seq,void * data)1359b028fb49SKurt Borja static int awcc_debugfs_system_description_read(struct seq_file *seq, void *data)
1360b028fb49SKurt Borja {
1361b028fb49SKurt Borja struct device *dev = seq->private;
1362b028fb49SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
1363b028fb49SKurt Borja
1364b028fb49SKurt Borja seq_printf(seq, "0x%08x\n", priv->system_description);
1365b028fb49SKurt Borja
1366b028fb49SKurt Borja return 0;
1367b028fb49SKurt Borja }
1368b028fb49SKurt Borja
awcc_debugfs_hwmon_data_read(struct seq_file * seq,void * data)1369b028fb49SKurt Borja static int awcc_debugfs_hwmon_data_read(struct seq_file *seq, void *data)
1370b028fb49SKurt Borja {
1371b028fb49SKurt Borja struct device *dev = seq->private;
1372b028fb49SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
1373b028fb49SKurt Borja const struct awcc_fan_data *fan;
1374b028fb49SKurt Borja unsigned int bit;
1375b028fb49SKurt Borja
1376b028fb49SKurt Borja seq_printf(seq, "Number of fans: %u\n", priv->fan_count);
1377b028fb49SKurt Borja seq_printf(seq, "Number of temperature sensors: %u\n\n", priv->temp_count);
1378b028fb49SKurt Borja
1379b028fb49SKurt Borja for (u32 i = 0; i < priv->fan_count; i++) {
1380b028fb49SKurt Borja fan = priv->fan_data[i];
1381b028fb49SKurt Borja
1382b028fb49SKurt Borja seq_printf(seq, "Fan %u:\n", i);
1383b028fb49SKurt Borja seq_printf(seq, " ID: 0x%02x\n", fan->id);
1384b028fb49SKurt Borja seq_printf(seq, " Related temperature sensors bitmap: %lu\n",
1385b028fb49SKurt Borja fan->auto_channels_temp);
1386b028fb49SKurt Borja }
1387b028fb49SKurt Borja
1388b028fb49SKurt Borja seq_puts(seq, "\nTemperature sensor IDs:\n");
1389b028fb49SKurt Borja for_each_set_bit(bit, priv->temp_sensors, AWCC_ID_BITMAP_SIZE)
1390b028fb49SKurt Borja seq_printf(seq, " 0x%02x\n", bit);
1391b028fb49SKurt Borja
1392b028fb49SKurt Borja return 0;
1393b028fb49SKurt Borja }
1394b028fb49SKurt Borja
awcc_debugfs_pprof_data_read(struct seq_file * seq,void * data)1395b028fb49SKurt Borja static int awcc_debugfs_pprof_data_read(struct seq_file *seq, void *data)
1396b028fb49SKurt Borja {
1397b028fb49SKurt Borja struct device *dev = seq->private;
1398b028fb49SKurt Borja struct awcc_priv *priv = dev_get_drvdata(dev);
1399b028fb49SKurt Borja
1400b028fb49SKurt Borja seq_printf(seq, "Number of thermal profiles: %u\n\n", priv->profile_count);
1401b028fb49SKurt Borja
1402b028fb49SKurt Borja for (u32 i = 0; i < PLATFORM_PROFILE_LAST; i++) {
1403b028fb49SKurt Borja if (!priv->supported_profiles[i])
1404b028fb49SKurt Borja continue;
1405b028fb49SKurt Borja
1406b028fb49SKurt Borja seq_printf(seq, "Platform profile %u:\n", i);
1407b028fb49SKurt Borja seq_printf(seq, " ID: 0x%02x\n", priv->supported_profiles[i]);
1408b028fb49SKurt Borja }
1409b028fb49SKurt Borja
1410b028fb49SKurt Borja return 0;
1411b028fb49SKurt Borja }
1412b028fb49SKurt Borja
awcc_gpio_pin_show(struct seq_file * seq,void * data)1413aee5cf93SKurt Borja static int awcc_gpio_pin_show(struct seq_file *seq, void *data)
1414aee5cf93SKurt Borja {
1415aee5cf93SKurt Borja unsigned long pin = debugfs_get_aux_num(seq->file);
1416aee5cf93SKurt Borja struct wmi_device *wdev = seq->private;
1417aee5cf93SKurt Borja u32 status;
1418aee5cf93SKurt Borja int ret;
1419aee5cf93SKurt Borja
1420aee5cf93SKurt Borja ret = awcc_read_gpio_status(wdev, pin, &status);
1421aee5cf93SKurt Borja if (ret)
1422aee5cf93SKurt Borja return ret;
1423aee5cf93SKurt Borja
1424aee5cf93SKurt Borja seq_printf(seq, "%u\n", status);
1425aee5cf93SKurt Borja
1426aee5cf93SKurt Borja return 0;
1427aee5cf93SKurt Borja }
1428aee5cf93SKurt Borja
awcc_gpio_pin_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)1429aee5cf93SKurt Borja static ssize_t awcc_gpio_pin_write(struct file *file, const char __user *buf,
1430aee5cf93SKurt Borja size_t count, loff_t *ppos)
1431aee5cf93SKurt Borja {
1432aee5cf93SKurt Borja unsigned long pin = debugfs_get_aux_num(file);
1433aee5cf93SKurt Borja struct seq_file *seq = file->private_data;
1434aee5cf93SKurt Borja struct wmi_device *wdev = seq->private;
1435aee5cf93SKurt Borja bool status;
1436aee5cf93SKurt Borja int ret;
1437aee5cf93SKurt Borja
1438aee5cf93SKurt Borja if (!ppos || *ppos)
1439aee5cf93SKurt Borja return -EINVAL;
1440aee5cf93SKurt Borja
1441aee5cf93SKurt Borja ret = kstrtobool_from_user(buf, count, &status);
1442aee5cf93SKurt Borja if (ret)
1443aee5cf93SKurt Borja return ret;
1444aee5cf93SKurt Borja
1445aee5cf93SKurt Borja ret = awcc_fwup_gpio_control(wdev, pin, status);
1446aee5cf93SKurt Borja if (ret)
1447aee5cf93SKurt Borja return ret;
1448aee5cf93SKurt Borja
1449aee5cf93SKurt Borja return count;
1450aee5cf93SKurt Borja }
1451aee5cf93SKurt Borja
1452aee5cf93SKurt Borja DEFINE_SHOW_STORE_ATTRIBUTE(awcc_gpio_pin);
1453aee5cf93SKurt Borja
awcc_debugfs_remove(void * data)1454b028fb49SKurt Borja static void awcc_debugfs_remove(void *data)
1455b028fb49SKurt Borja {
1456b028fb49SKurt Borja struct dentry *root = data;
1457b028fb49SKurt Borja
1458b028fb49SKurt Borja debugfs_remove(root);
1459b028fb49SKurt Borja }
1460b028fb49SKurt Borja
awcc_debugfs_init(struct wmi_device * wdev)1461b028fb49SKurt Borja static void awcc_debugfs_init(struct wmi_device *wdev)
1462b028fb49SKurt Borja {
1463aee5cf93SKurt Borja struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
1464aee5cf93SKurt Borja struct dentry *root, *gpio_ctl;
1465aee5cf93SKurt Borja u32 gpio_count;
1466b028fb49SKurt Borja char name[64];
1467aee5cf93SKurt Borja int ret;
1468b028fb49SKurt Borja
1469b028fb49SKurt Borja scnprintf(name, sizeof(name), "%s-%s", "alienware-wmi", dev_name(&wdev->dev));
1470b028fb49SKurt Borja root = debugfs_create_dir(name, NULL);
1471b028fb49SKurt Borja
1472b028fb49SKurt Borja debugfs_create_devm_seqfile(&wdev->dev, "system_description", root,
1473b028fb49SKurt Borja awcc_debugfs_system_description_read);
1474b028fb49SKurt Borja
1475b028fb49SKurt Borja if (awcc->hwmon)
1476b028fb49SKurt Borja debugfs_create_devm_seqfile(&wdev->dev, "hwmon_data", root,
1477b028fb49SKurt Borja awcc_debugfs_hwmon_data_read);
1478b028fb49SKurt Borja
1479b028fb49SKurt Borja if (awcc->pprof)
1480b028fb49SKurt Borja debugfs_create_devm_seqfile(&wdev->dev, "pprof_data", root,
1481b028fb49SKurt Borja awcc_debugfs_pprof_data_read);
1482b028fb49SKurt Borja
1483aee5cf93SKurt Borja ret = awcc_read_total_gpios(wdev, &gpio_count);
1484aee5cf93SKurt Borja if (ret) {
1485aee5cf93SKurt Borja dev_dbg(&wdev->dev, "Failed to get total GPIO Pin count\n");
1486aee5cf93SKurt Borja goto out_add_action;
1487aee5cf93SKurt Borja } else if (gpio_count > AWCC_MAX_RES_COUNT) {
1488aee5cf93SKurt Borja dev_dbg(&wdev->dev, "Reported GPIO Pin count may be incorrect: %u\n", gpio_count);
1489aee5cf93SKurt Borja goto out_add_action;
1490aee5cf93SKurt Borja }
1491aee5cf93SKurt Borja
1492aee5cf93SKurt Borja gpio_ctl = debugfs_create_dir("gpio_ctl", root);
1493aee5cf93SKurt Borja
1494aee5cf93SKurt Borja priv->gpio_count = gpio_count;
1495aee5cf93SKurt Borja debugfs_create_u32("total_gpios", 0444, gpio_ctl, &priv->gpio_count);
1496aee5cf93SKurt Borja
1497aee5cf93SKurt Borja for (unsigned int i = 0; i < gpio_count; i++) {
1498aee5cf93SKurt Borja scnprintf(name, sizeof(name), "pin%u", i);
1499aee5cf93SKurt Borja debugfs_create_file_aux_num(name, 0644, gpio_ctl, wdev, i,
1500aee5cf93SKurt Borja &awcc_gpio_pin_fops);
1501aee5cf93SKurt Borja }
1502aee5cf93SKurt Borja
1503aee5cf93SKurt Borja out_add_action:
1504b028fb49SKurt Borja devm_add_action_or_reset(&wdev->dev, awcc_debugfs_remove, root);
1505b028fb49SKurt Borja }
1506b028fb49SKurt Borja
alienware_awcc_setup(struct wmi_device * wdev)15078cc2c415SKurt Borja static int alienware_awcc_setup(struct wmi_device *wdev)
15088cc2c415SKurt Borja {
15098cc2c415SKurt Borja struct awcc_priv *priv;
15108cc2c415SKurt Borja int ret;
15118cc2c415SKurt Borja
15128cc2c415SKurt Borja priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
15138cc2c415SKurt Borja if (!priv)
15148cc2c415SKurt Borja return -ENOMEM;
15158cc2c415SKurt Borja
151632b6372dSKurt Borja ret = awcc_thermal_information(wdev, AWCC_OP_GET_SYSTEM_DESCRIPTION,
151732b6372dSKurt Borja 0, &priv->system_description);
151832b6372dSKurt Borja if (ret < 0)
151932b6372dSKurt Borja return ret;
152032b6372dSKurt Borja
152132b6372dSKurt Borja /* Sanity check */
152232b6372dSKurt Borja for (unsigned int i = 0; i < ARRAY_SIZE(priv->res_count); i++) {
152332b6372dSKurt Borja if (priv->res_count[i] > AWCC_MAX_RES_COUNT) {
152432b6372dSKurt Borja dev_err(&wdev->dev, "Malformed system description: 0x%08x\n",
152532b6372dSKurt Borja priv->system_description);
152632b6372dSKurt Borja return -ENXIO;
152732b6372dSKurt Borja }
152832b6372dSKurt Borja }
152932b6372dSKurt Borja
15308cc2c415SKurt Borja priv->wdev = wdev;
15318cc2c415SKurt Borja dev_set_drvdata(&wdev->dev, priv);
15328cc2c415SKurt Borja
1533d6999078SKurt Borja if (awcc->hwmon) {
1534d6999078SKurt Borja ret = awcc_hwmon_init(wdev);
1535d6999078SKurt Borja if (ret)
1536d6999078SKurt Borja return ret;
1537d6999078SKurt Borja }
1538d6999078SKurt Borja
15398cc2c415SKurt Borja if (awcc->pprof) {
15408cc2c415SKurt Borja ret = awcc_platform_profile_init(wdev);
15418cc2c415SKurt Borja if (ret)
15428cc2c415SKurt Borja return ret;
15438cc2c415SKurt Borja }
15448cc2c415SKurt Borja
1545b028fb49SKurt Borja awcc_debugfs_init(wdev);
1546b028fb49SKurt Borja
15478cc2c415SKurt Borja return 0;
15488cc2c415SKurt Borja }
15498cc2c415SKurt Borja
15508cc2c415SKurt Borja /*
15518cc2c415SKurt Borja * WMAX WMI driver
15528cc2c415SKurt Borja */
wmax_wmi_update_led(struct alienfx_priv * priv,struct wmi_device * wdev,u8 location)15538cc2c415SKurt Borja static int wmax_wmi_update_led(struct alienfx_priv *priv,
15548cc2c415SKurt Borja struct wmi_device *wdev, u8 location)
15558cc2c415SKurt Borja {
15568cc2c415SKurt Borja struct wmax_led_args in_args = {
15578cc2c415SKurt Borja .led_mask = 1 << location,
15588cc2c415SKurt Borja .colors = priv->colors[location],
15598cc2c415SKurt Borja .state = priv->lighting_control_state,
15608cc2c415SKurt Borja };
15618cc2c415SKurt Borja
15628cc2c415SKurt Borja return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args,
15638cc2c415SKurt Borja sizeof(in_args), NULL);
15648cc2c415SKurt Borja }
15658cc2c415SKurt Borja
wmax_wmi_update_brightness(struct alienfx_priv * priv,struct wmi_device * wdev,u8 brightness)15668cc2c415SKurt Borja static int wmax_wmi_update_brightness(struct alienfx_priv *priv,
15678cc2c415SKurt Borja struct wmi_device *wdev, u8 brightness)
15688cc2c415SKurt Borja {
15698cc2c415SKurt Borja struct wmax_brightness_args in_args = {
15708cc2c415SKurt Borja .led_mask = 0xFF,
15718cc2c415SKurt Borja .percentage = brightness,
15728cc2c415SKurt Borja };
15738cc2c415SKurt Borja
15748cc2c415SKurt Borja return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args,
15758cc2c415SKurt Borja sizeof(in_args), NULL);
15768cc2c415SKurt Borja }
15778cc2c415SKurt Borja
wmax_wmi_probe(struct wmi_device * wdev,const void * context)15788cc2c415SKurt Borja static int wmax_wmi_probe(struct wmi_device *wdev, const void *context)
15798cc2c415SKurt Borja {
15808cc2c415SKurt Borja struct alienfx_platdata pdata = {
15818cc2c415SKurt Borja .wdev = wdev,
15828cc2c415SKurt Borja .ops = {
15838cc2c415SKurt Borja .upd_led = wmax_wmi_update_led,
15848cc2c415SKurt Borja .upd_brightness = wmax_wmi_update_brightness,
15858cc2c415SKurt Borja },
15868cc2c415SKurt Borja };
15878cc2c415SKurt Borja int ret;
15888cc2c415SKurt Borja
15898cc2c415SKurt Borja if (awcc)
15908cc2c415SKurt Borja ret = alienware_awcc_setup(wdev);
15918cc2c415SKurt Borja else
15928cc2c415SKurt Borja ret = alienware_alienfx_setup(&pdata);
15938cc2c415SKurt Borja
15948cc2c415SKurt Borja return ret;
15958cc2c415SKurt Borja }
15968cc2c415SKurt Borja
wmax_wmi_suspend(struct device * dev)159707ac2759SKurt Borja static int wmax_wmi_suspend(struct device *dev)
159807ac2759SKurt Borja {
159907ac2759SKurt Borja if (awcc->hwmon)
160007ac2759SKurt Borja awcc_hwmon_suspend(dev);
160107ac2759SKurt Borja
160207ac2759SKurt Borja return 0;
160307ac2759SKurt Borja }
160407ac2759SKurt Borja
wmax_wmi_resume(struct device * dev)160507ac2759SKurt Borja static int wmax_wmi_resume(struct device *dev)
160607ac2759SKurt Borja {
160707ac2759SKurt Borja if (awcc->hwmon)
160807ac2759SKurt Borja awcc_hwmon_resume(dev);
160907ac2759SKurt Borja
161007ac2759SKurt Borja return 0;
161107ac2759SKurt Borja }
161207ac2759SKurt Borja
161307ac2759SKurt Borja static DEFINE_SIMPLE_DEV_PM_OPS(wmax_wmi_pm_ops, wmax_wmi_suspend, wmax_wmi_resume);
161407ac2759SKurt Borja
16158cc2c415SKurt Borja static const struct wmi_device_id alienware_wmax_device_id_table[] = {
16168cc2c415SKurt Borja { WMAX_CONTROL_GUID, NULL },
16178cc2c415SKurt Borja { },
16188cc2c415SKurt Borja };
16198cc2c415SKurt Borja MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table);
16208cc2c415SKurt Borja
16218cc2c415SKurt Borja static struct wmi_driver alienware_wmax_wmi_driver = {
16228cc2c415SKurt Borja .driver = {
16238cc2c415SKurt Borja .name = "alienware-wmi-wmax",
16248cc2c415SKurt Borja .probe_type = PROBE_PREFER_ASYNCHRONOUS,
162507ac2759SKurt Borja .pm = pm_sleep_ptr(&wmax_wmi_pm_ops),
16268cc2c415SKurt Borja },
16278cc2c415SKurt Borja .id_table = alienware_wmax_device_id_table,
16288cc2c415SKurt Borja .probe = wmax_wmi_probe,
16298cc2c415SKurt Borja .no_singleton = true,
16308cc2c415SKurt Borja };
16318cc2c415SKurt Borja
alienware_wmax_wmi_init(void)16328cc2c415SKurt Borja int __init alienware_wmax_wmi_init(void)
16338cc2c415SKurt Borja {
16348cc2c415SKurt Borja const struct dmi_system_id *id;
16358cc2c415SKurt Borja
16368cc2c415SKurt Borja id = dmi_first_match(awcc_dmi_table);
16378cc2c415SKurt Borja if (id)
16388cc2c415SKurt Borja awcc = id->driver_data;
16398cc2c415SKurt Borja
1640d6999078SKurt Borja if (force_hwmon) {
1641d6999078SKurt Borja if (!awcc)
1642d6999078SKurt Borja awcc = &empty_quirks;
1643d6999078SKurt Borja
1644d6999078SKurt Borja awcc->hwmon = true;
1645d6999078SKurt Borja }
1646d6999078SKurt Borja
16478cc2c415SKurt Borja if (force_platform_profile) {
16488cc2c415SKurt Borja if (!awcc)
16498cc2c415SKurt Borja awcc = &empty_quirks;
16508cc2c415SKurt Borja
16518cc2c415SKurt Borja awcc->pprof = true;
16528cc2c415SKurt Borja }
16538cc2c415SKurt Borja
16548cc2c415SKurt Borja if (force_gmode) {
16558cc2c415SKurt Borja if (awcc)
16568cc2c415SKurt Borja awcc->gmode = true;
16578cc2c415SKurt Borja else
16588cc2c415SKurt Borja pr_warn("force_gmode requires platform profile support\n");
16598cc2c415SKurt Borja }
16608cc2c415SKurt Borja
16618cc2c415SKurt Borja return wmi_driver_register(&alienware_wmax_wmi_driver);
16628cc2c415SKurt Borja }
16638cc2c415SKurt Borja
alienware_wmax_wmi_exit(void)16648cc2c415SKurt Borja void __exit alienware_wmax_wmi_exit(void)
16658cc2c415SKurt Borja {
16668cc2c415SKurt Borja wmi_driver_unregister(&alienware_wmax_wmi_driver);
16678cc2c415SKurt Borja }
1668