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: ayaneo-ec: Add hwmon support

Add hwmon single fan sensor reads and control for Ayaneo devices.
The register and method of access is the same for all devices.

Reviewed-by: Armin-Wolf <W_Armin@gmx.de>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
Link: https://patch.msgid.link/20251119174505.597218-3-lkml@antheas.dev
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Antheas Kapenekakis and committed by
Ilpo Järvinen
536522f0 70a4a815

+138
+2
drivers/platform/x86/Kconfig
··· 314 314 config AYANEO_EC 315 315 tristate "Ayaneo EC platform control" 316 316 depends on DMI 317 + depends on ACPI_EC 318 + depends on HWMON 317 319 help 318 320 Enables support for the platform EC of Ayaneo devices. This 319 321 includes fan control, fan speed, charge limit, magic
+136
drivers/platform/x86/ayaneo-ec.c
··· 7 7 * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev> 8 8 */ 9 9 10 + #include <linux/acpi.h> 10 11 #include <linux/dmi.h> 11 12 #include <linux/err.h> 13 + #include <linux/hwmon.h> 12 14 #include <linux/init.h> 13 15 #include <linux/kernel.h> 14 16 #include <linux/module.h> 15 17 #include <linux/platform_device.h> 16 18 19 + #define AYANEO_PWM_ENABLE_REG 0x4A 20 + #define AYANEO_PWM_REG 0x4B 21 + #define AYANEO_PWM_MODE_AUTO 0x00 22 + #define AYANEO_PWM_MODE_MANUAL 0x01 23 + 24 + #define AYANEO_FAN_REG 0x76 25 + 17 26 struct ayaneo_ec_quirk { 27 + bool has_fan_control; 18 28 }; 19 29 20 30 struct ayaneo_ec_platform_data { ··· 33 23 }; 34 24 35 25 static const struct ayaneo_ec_quirk quirk_ayaneo3 = { 26 + .has_fan_control = true, 36 27 }; 37 28 38 29 static const struct dmi_system_id dmi_table[] = { ··· 47 36 {}, 48 37 }; 49 38 39 + /* Callbacks for hwmon interface */ 40 + static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata, 41 + enum hwmon_sensor_types type, u32 attr, 42 + int channel) 43 + { 44 + switch (type) { 45 + case hwmon_fan: 46 + return 0444; 47 + case hwmon_pwm: 48 + return 0644; 49 + default: 50 + return 0; 51 + } 52 + } 53 + 54 + static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type, 55 + u32 attr, int channel, long *val) 56 + { 57 + u8 tmp; 58 + int ret; 59 + 60 + switch (type) { 61 + case hwmon_fan: 62 + switch (attr) { 63 + case hwmon_fan_input: 64 + ret = ec_read(AYANEO_FAN_REG, &tmp); 65 + if (ret) 66 + return ret; 67 + *val = tmp << 8; 68 + ret = ec_read(AYANEO_FAN_REG + 1, &tmp); 69 + if (ret) 70 + return ret; 71 + *val |= tmp; 72 + return 0; 73 + default: 74 + break; 75 + } 76 + break; 77 + case hwmon_pwm: 78 + switch (attr) { 79 + case hwmon_pwm_input: 80 + ret = ec_read(AYANEO_PWM_REG, &tmp); 81 + if (ret) 82 + return ret; 83 + if (tmp > 100) 84 + return -EIO; 85 + *val = (255 * tmp) / 100; 86 + return 0; 87 + case hwmon_pwm_enable: 88 + ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); 89 + if (ret) 90 + return ret; 91 + if (tmp == AYANEO_PWM_MODE_MANUAL) 92 + *val = 1; 93 + else if (tmp == AYANEO_PWM_MODE_AUTO) 94 + *val = 2; 95 + else 96 + return -EIO; 97 + return 0; 98 + default: 99 + break; 100 + } 101 + break; 102 + default: 103 + break; 104 + } 105 + return -EOPNOTSUPP; 106 + } 107 + 108 + static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type, 109 + u32 attr, int channel, long val) 110 + { 111 + switch (type) { 112 + case hwmon_pwm: 113 + switch (attr) { 114 + case hwmon_pwm_enable: 115 + switch (val) { 116 + case 1: 117 + return ec_write(AYANEO_PWM_ENABLE_REG, 118 + AYANEO_PWM_MODE_MANUAL); 119 + case 2: 120 + return ec_write(AYANEO_PWM_ENABLE_REG, 121 + AYANEO_PWM_MODE_AUTO); 122 + default: 123 + return -EINVAL; 124 + } 125 + case hwmon_pwm_input: 126 + if (val < 0 || val > 255) 127 + return -EINVAL; 128 + return ec_write(AYANEO_PWM_REG, (val * 100) / 255); 129 + default: 130 + break; 131 + } 132 + break; 133 + default: 134 + break; 135 + } 136 + return -EOPNOTSUPP; 137 + } 138 + 139 + static const struct hwmon_ops ayaneo_ec_hwmon_ops = { 140 + .is_visible = ayaneo_ec_hwmon_is_visible, 141 + .read = ayaneo_ec_read, 142 + .write = ayaneo_ec_write, 143 + }; 144 + 145 + static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = { 146 + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), 147 + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 148 + NULL, 149 + }; 150 + 151 + static const struct hwmon_chip_info ayaneo_ec_chip_info = { 152 + .ops = &ayaneo_ec_hwmon_ops, 153 + .info = ayaneo_ec_sensors, 154 + }; 155 + 50 156 static int ayaneo_ec_probe(struct platform_device *pdev) 51 157 { 52 158 const struct dmi_system_id *dmi_entry; 53 159 struct ayaneo_ec_platform_data *data; 160 + struct device *hwdev; 54 161 55 162 dmi_entry = dmi_first_match(dmi_table); 56 163 if (!dmi_entry) ··· 181 52 data->pdev = pdev; 182 53 data->quirks = dmi_entry->driver_data; 183 54 platform_set_drvdata(pdev, data); 55 + 56 + if (data->quirks->has_fan_control) { 57 + hwdev = devm_hwmon_device_register_with_info(&pdev->dev, 58 + "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL); 59 + if (IS_ERR(hwdev)) 60 + return PTR_ERR(hwdev); 61 + } 184 62 185 63 return 0; 186 64 }