platform/x86: system76_acpi: Report temperature and fan speed
Add a hwmon interface to report CPU/GPU temperature and fan speed. sensors now reports an ACPI interface with the entries: system76_acpi-acpi-0 Adapter: ACPI interface CPU fan: 0 RPM GPU fan: 0 RPM CPU temp: +47.0°C GPU temp: +0.0°C Signed-off-by: Jeremy Soller <jeremy@system76.com> Signed-off-by: Tim Crawford <tcrawford@system76.com> Link: https://lore.kernel.org/r/20211006202202.7479-2-tcrawford@system76.com Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
249606d37d
commit
95563d45b5
@ -10,6 +10,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
@ -24,6 +26,9 @@ struct system76_data {
|
|||||||
enum led_brightness kb_brightness;
|
enum led_brightness kb_brightness;
|
||||||
enum led_brightness kb_toggle_brightness;
|
enum led_brightness kb_toggle_brightness;
|
||||||
int kb_color;
|
int kb_color;
|
||||||
|
struct device *therm;
|
||||||
|
union acpi_object *nfan;
|
||||||
|
union acpi_object *ntmp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct acpi_device_id device_ids[] = {
|
static const struct acpi_device_id device_ids[] = {
|
||||||
@ -63,9 +68,57 @@ static int system76_get(struct system76_data *data, char *method)
|
|||||||
handle = acpi_device_handle(data->acpi_dev);
|
handle = acpi_device_handle(data->acpi_dev);
|
||||||
status = acpi_evaluate_integer(handle, method, NULL, &ret);
|
status = acpi_evaluate_integer(handle, method, NULL, &ret);
|
||||||
if (ACPI_SUCCESS(status))
|
if (ACPI_SUCCESS(status))
|
||||||
return (int)ret;
|
return ret;
|
||||||
else
|
return -ENODEV;
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
// Get a System76 ACPI device value by name with index
|
||||||
|
static int system76_get_index(struct system76_data *data, char *method, int index)
|
||||||
|
{
|
||||||
|
union acpi_object obj;
|
||||||
|
struct acpi_object_list obj_list;
|
||||||
|
acpi_handle handle;
|
||||||
|
acpi_status status;
|
||||||
|
unsigned long long ret = 0;
|
||||||
|
|
||||||
|
obj.type = ACPI_TYPE_INTEGER;
|
||||||
|
obj.integer.value = index;
|
||||||
|
obj_list.count = 1;
|
||||||
|
obj_list.pointer = &obj;
|
||||||
|
|
||||||
|
handle = acpi_device_handle(data->acpi_dev);
|
||||||
|
status = acpi_evaluate_integer(handle, method, &obj_list, &ret);
|
||||||
|
if (ACPI_SUCCESS(status))
|
||||||
|
return ret;
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a System76 ACPI device object by name
|
||||||
|
static int system76_get_object(struct system76_data *data, char *method, union acpi_object **obj)
|
||||||
|
{
|
||||||
|
acpi_handle handle;
|
||||||
|
acpi_status status;
|
||||||
|
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
|
||||||
|
handle = acpi_device_handle(data->acpi_dev);
|
||||||
|
status = acpi_evaluate_object(handle, method, NULL, &buf);
|
||||||
|
if (ACPI_SUCCESS(status)) {
|
||||||
|
*obj = buf.pointer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a name from a System76 ACPI device object
|
||||||
|
static char *system76_name(union acpi_object *obj, int index)
|
||||||
|
{
|
||||||
|
if (obj && obj->type == ACPI_TYPE_PACKAGE && index <= obj->package.count) {
|
||||||
|
if (obj->package.elements[index].type == ACPI_TYPE_STRING)
|
||||||
|
return obj->package.elements[index].string.pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a System76 ACPI device value by name
|
// Set a System76 ACPI device value by name
|
||||||
@ -270,6 +323,146 @@ static void kb_led_hotkey_color(struct system76_data *data)
|
|||||||
kb_led_notify(data);
|
kb_led_notify(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static umode_t thermal_is_visible(const void *drvdata, enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel)
|
||||||
|
{
|
||||||
|
const struct system76_data *data = drvdata;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case hwmon_fan:
|
||||||
|
case hwmon_pwm:
|
||||||
|
if (system76_name(data->nfan, channel))
|
||||||
|
return 0444;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hwmon_temp:
|
||||||
|
if (system76_name(data->ntmp, channel))
|
||||||
|
return 0444;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int thermal_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||||
|
int channel, long *val)
|
||||||
|
{
|
||||||
|
struct system76_data *data = dev_get_drvdata(dev);
|
||||||
|
int raw;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case hwmon_fan:
|
||||||
|
if (attr == hwmon_fan_input) {
|
||||||
|
raw = system76_get_index(data, "GFAN", channel);
|
||||||
|
if (raw < 0)
|
||||||
|
return raw;
|
||||||
|
*val = (raw >> 8) & 0xFFFF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hwmon_pwm:
|
||||||
|
if (attr == hwmon_pwm_input) {
|
||||||
|
raw = system76_get_index(data, "GFAN", channel);
|
||||||
|
if (raw < 0)
|
||||||
|
return raw;
|
||||||
|
*val = raw & 0xFF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hwmon_temp:
|
||||||
|
if (attr == hwmon_temp_input) {
|
||||||
|
raw = system76_get_index(data, "GTMP", channel);
|
||||||
|
if (raw < 0)
|
||||||
|
return raw;
|
||||||
|
*val = raw * 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int thermal_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||||
|
int channel, const char **str)
|
||||||
|
{
|
||||||
|
struct system76_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case hwmon_fan:
|
||||||
|
if (attr == hwmon_fan_label) {
|
||||||
|
*str = system76_name(data->nfan, channel);
|
||||||
|
if (*str)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hwmon_temp:
|
||||||
|
if (attr == hwmon_temp_label) {
|
||||||
|
*str = system76_name(data->ntmp, channel);
|
||||||
|
if (*str)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct hwmon_ops thermal_ops = {
|
||||||
|
.is_visible = thermal_is_visible,
|
||||||
|
.read = thermal_read,
|
||||||
|
.read_string = thermal_read_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate up to 8 fans and temperatures
|
||||||
|
static const struct hwmon_channel_info *thermal_channel_info[] = {
|
||||||
|
HWMON_CHANNEL_INFO(fan,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||||
|
HWMON_F_INPUT | HWMON_F_LABEL),
|
||||||
|
HWMON_CHANNEL_INFO(pwm,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT,
|
||||||
|
HWMON_PWM_INPUT),
|
||||||
|
HWMON_CHANNEL_INFO(temp,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||||
|
HWMON_T_INPUT | HWMON_T_LABEL),
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_chip_info thermal_chip_info = {
|
||||||
|
.ops = &thermal_ops,
|
||||||
|
.info = thermal_channel_info,
|
||||||
|
};
|
||||||
|
|
||||||
// Handle ACPI notification
|
// Handle ACPI notification
|
||||||
static void system76_notify(struct acpi_device *acpi_dev, u32 event)
|
static void system76_notify(struct acpi_device *acpi_dev, u32 event)
|
||||||
{
|
{
|
||||||
@ -346,7 +539,26 @@ static int system76_add(struct acpi_device *acpi_dev)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = system76_get_object(data, "NFAN", &data->nfan);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
err = system76_get_object(data, "NTMP", &data->ntmp);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev,
|
||||||
|
"system76_acpi", data, &thermal_chip_info, NULL);
|
||||||
|
err = PTR_ERR_OR_ZERO(data->therm);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
kfree(data->ntmp);
|
||||||
|
kfree(data->nfan);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a System76 ACPI device
|
// Remove a System76 ACPI device
|
||||||
@ -359,9 +571,11 @@ static int system76_remove(struct acpi_device *acpi_dev)
|
|||||||
device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr);
|
device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr);
|
||||||
|
|
||||||
devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led);
|
devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led);
|
||||||
|
|
||||||
devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led);
|
devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led);
|
||||||
|
|
||||||
|
kfree(data->nfan);
|
||||||
|
kfree(data->ntmp);
|
||||||
|
|
||||||
system76_get(data, "FINI");
|
system76_get(data, "FINI");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user