UBUNTU: ODM: hwmon: add driver for AAEON devices

BugLink: https://bugs.launchpad.net/bugs/1929504

This refator patch adds support for the hwmon information
which are transported to userspace through ASUS WMI interface.

Signed-off-by: Kunyang_Fan <kunyang_fan@asus.com>
Review-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
Review-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
This commit is contained in:
Kunyang_Fan
2021-06-16 13:57:01 +08:00
committed by Paolo Pisati
parent 8f49f5e43b
commit 1e6e88c0d9
3 changed files with 581 additions and 0 deletions
+12
View File
@@ -38,6 +38,18 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
config SENSORS_AAEON
tristate "AAEON hwmon driver"
depends on X86
depends on UBUNTU_ODM_DRIVERS
select MFD_AAEON
help
This hwmon driver adds support for reporting temperature or fan
speed and voltage on Single Board Computers produced by AAEON.
This driver leverages the ASUS WMI interface to access device
resources.
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
depends on (X86 && DMI) || COMPILE_TEST
+1
View File
@@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_HP_WMI) += hp-wmi-sensors.o
# Native drivers
# asb100, then w83781d go first, as they can override other drivers' addresses.
obj-$(CONFIG_SENSORS_AAEON) += hwmon-aaeon.o
obj-$(CONFIG_SENSORS_ASB100) += asb100.o
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83773G) += w83773g.o
+568
View File
@@ -0,0 +1,568 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AAEON HWMON driver
* Copyright (c) 2021, AAEON Ltd.
*
* Author: Edward Lin <edward1_lin@aaeon.com.tw>
* Author: Kunyang Fan <kunyang_fan@aaeon.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_data/x86/asus-wmi.h>
#include <linux/platform_device.h>
#define DRVNAME "hwmon-aaeon"
#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
#define AAEON_VERSION_METHOD_ID 0x00000000
#define HWM_INFORMATION_METHOD_ID 0x00030000
#define HWM_METHOD_ID 0x00030001
#define BITMAP_TEMP_ARG 0x12
#define BITMAP_FAN_ARG 0x13
#define BITMAP_VOLTAGE_ARG 0x14
#define SENSOR_TEMP_NUMBER 0
#define SENSOR_FAN_NUMBER 1
#define SENSOR_VOLTAGE_NUMBER 2
#define SENSOR_MAX_NUMBER 2
static ssize_t aaeon_show_sensor(struct device *dev,
struct device_attribute *devattr, char *buf);
static ssize_t aaeon_show_sensor_name(struct device *dev,
struct device_attribute *devattr,
char *buf);
static ssize_t aaeon_show_version(struct device *dev,
struct device_attribute *devattr, char *buf);
static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
char *buf);
static int aaeon_get_version(void);
static int aaeon_hwmon_probe(struct platform_device *pdev);
static int aaeon_hwmon_remove(struct platform_device *pdev);
static const char * const temp_sensors_name_table[] = {
"CPU_Temp",
"SYS1_Temp",
"SYS2_Temp",
};
static const char * const temp_sensors_name_table_V3[] = {
"SYS_Temp",
"CPU_Temp",
};
static const char * const fan_sensors_name_table[] = {
"CPU_FAN",
"SYS1_FAN",
"SYS2_FAN",
"Chasis1_FAN",
"Chasis2_FAN",
};
static const char * const fan_sensors_name_table_V3[] = {
"Chasis_FAN",
"CPU_FAN",
};
static const char * const voltage_sensors_name_table[] = {
"VCORE_Voltage",
"VMEM_Voltage",
"+12_Voltage",
"+5_Voltage",
"+3.3_Voltage",
"+1.8_Voltage",
"5VSB_Voltage",
"3VSB_Voltage",
"VBAT_Voltage",
};
static const char * const voltage_sensors_name_table_V3[] = {
"VCORE_Voltage",
"+5_Voltage",
"AVCC_Voltage",
"+3.3_Voltage",
"+12_Voltage",
"VCOREREFIN_Voltage",
"VIN4_Voltage",
"3VSB_Voltage",
"VBAT_Voltage",
};
struct aaeon_hwmon_data {
struct device *hwmon_dev;
int bfpi_version;
u32 temp_bitmap;
u32 fan_bitmap;
u32 voltage_bitmap;
unsigned int sensors_number[SENSOR_MAX_NUMBER + 1];
const char * const *temp_names;
const char * const *fan_names;
const char * const *voltage_names;
};
/* Temperature attributes */
static struct sensor_device_attribute_2 temp_sys_nodes_atts[] = {
SENSOR_ATTR_2(temp1_input, 0444, aaeon_show_sensor, NULL,
SENSOR_TEMP_NUMBER, 0),
SENSOR_ATTR_2(temp1_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_TEMP_NUMBER, 0),
SENSOR_ATTR_2(temp2_input, 0444, aaeon_show_sensor, NULL,
SENSOR_TEMP_NUMBER, 1),
SENSOR_ATTR_2(temp2_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_TEMP_NUMBER, 1),
SENSOR_ATTR_2(temp3_input, 0444, aaeon_show_sensor, NULL,
SENSOR_TEMP_NUMBER, 2),
SENSOR_ATTR_2(temp3_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_TEMP_NUMBER, 2),
};
/* Cooler Fan attributes */
static struct sensor_device_attribute_2 fan_sys_nodes_atts[] = {
SENSOR_ATTR_2(fan1_input, 0444, aaeon_show_sensor, NULL,
SENSOR_FAN_NUMBER, 0),
SENSOR_ATTR_2(fan1_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_FAN_NUMBER, 0),
SENSOR_ATTR_2(fan2_input, 0444, aaeon_show_sensor, NULL,
SENSOR_FAN_NUMBER, 1),
SENSOR_ATTR_2(fan2_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_FAN_NUMBER, 1),
SENSOR_ATTR_2(fan3_input, 0444, aaeon_show_sensor, NULL,
SENSOR_FAN_NUMBER, 2),
SENSOR_ATTR_2(fan3_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_FAN_NUMBER, 2),
SENSOR_ATTR_2(fan4_input, 0444, aaeon_show_sensor, NULL,
SENSOR_FAN_NUMBER, 3),
SENSOR_ATTR_2(fan4_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_FAN_NUMBER, 3),
SENSOR_ATTR_2(fan5_input, 0444, aaeon_show_sensor, NULL,
SENSOR_FAN_NUMBER, 4),
SENSOR_ATTR_2(fan5_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_FAN_NUMBER, 4),
};
/* Voltage attributes */
static struct sensor_device_attribute_2 voltage_sys_nodes_atts[] = {
SENSOR_ATTR_2(in1_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 0),
SENSOR_ATTR_2(in1_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 0),
SENSOR_ATTR_2(in2_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 1),
SENSOR_ATTR_2(in2_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 1),
SENSOR_ATTR_2(in3_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 2),
SENSOR_ATTR_2(in3_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 2),
SENSOR_ATTR_2(in4_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 3),
SENSOR_ATTR_2(in4_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 3),
SENSOR_ATTR_2(in5_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 4),
SENSOR_ATTR_2(in5_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 4),
SENSOR_ATTR_2(in6_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 5),
SENSOR_ATTR_2(in6_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 5),
SENSOR_ATTR_2(in7_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 6),
SENSOR_ATTR_2(in7_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 6),
SENSOR_ATTR_2(in8_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 7),
SENSOR_ATTR_2(in8_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 7),
SENSOR_ATTR_2(in9_input, 0444, aaeon_show_sensor, NULL,
SENSOR_VOLTAGE_NUMBER, 8),
SENSOR_ATTR_2(in9_label, 0444, aaeon_show_sensor_name, NULL,
SENSOR_VOLTAGE_NUMBER, 8),
};
static struct sensor_device_attribute_2 info_sys_nodes_atts[] = {
/* WMI version Information */
SENSOR_ATTR_2(AAEON_VERSION, 0444, aaeon_show_version, NULL, 0, 0),
};
DEVICE_ATTR_RO(name);
static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, "%s\n", DRVNAME);
}
static ssize_t aaeon_show_version(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct aaeon_hwmon_data *data =
(struct aaeon_hwmon_data *)dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->bfpi_version);
}
static ssize_t aaeon_show_sensor_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u8 nr = to_sensor_dev_attr_2(devattr)->nr;
u8 index = to_sensor_dev_attr_2(devattr)->index;
struct aaeon_hwmon_data *data =
(struct aaeon_hwmon_data *)dev_get_drvdata(dev);
if (nr > SENSOR_MAX_NUMBER || index >= data->sensors_number[nr]) {
pr_debug("Can not check the device");
return -1;
}
switch (nr) {
case SENSOR_TEMP_NUMBER:
return sprintf(buf, "%s\n", data->temp_names[index]);
case SENSOR_FAN_NUMBER:
return sprintf(buf, "%s\n", data->fan_names[index]);
case SENSOR_VOLTAGE_NUMBER:
return sprintf(buf, "%s\n", data->voltage_names[index]);
default:
break;
}
return 0;
}
static ssize_t aaeon_show_sensor(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u8 nr = to_sensor_dev_attr_2(devattr)->nr;
u8 index = to_sensor_dev_attr_2(devattr)->index;
u32 dev_id;
int retval, err;
struct aaeon_hwmon_data *data =
(struct aaeon_hwmon_data *)dev_get_drvdata(dev);
if (nr > SENSOR_MAX_NUMBER || index >= data->sensors_number[nr]) {
pr_debug("Can not check the device");
return -1;
}
/* For the V3 version, index need offset */
if (data->bfpi_version == 0x03 && nr != SENSOR_VOLTAGE_NUMBER)
index++;
dev_id = (index << 12) | (nr << 8);
err = asus_wmi_evaluate_method(HWM_METHOD_ID, dev_id, 0, &retval);
if (err)
return err;
/* For the V3 version, need to convert the raw value*/
if (nr == SENSOR_VOLTAGE_NUMBER && data->bfpi_version == 0x03) {
switch (index) {
case 0: /* VCORE */
retval = retval * 16;
break;
case 1: /* +5V */
retval = (retval * 2008) / 50;
break;
case 2: /* AVCC */
retval = retval * 16;
break;
case 3: /* +3.3V */
retval = retval * 16;
break;
case 4: /* +12V */
retval = retval * 96;
break;
case 5: /* VCOREREFIN */
retval = (retval * 552) / 41;
break;
case 6: /* VIN4 */
retval = retval * 8;
break;
case 7: /* 3VSB */
retval = retval * 16;
break;
case 8: /* VBAT */
retval = retval * 16;
break;
default:
break;
}
} else if (nr == SENSOR_TEMP_NUMBER && data->bfpi_version == 0x03)
retval = retval * 1000;
return sprintf(buf, "%d\n", retval);
}
static int aaeon_hwmon_create_sub_sysfs_fs(struct platform_device *pdev,
struct sensor_device_attribute_2 *attr,
int sensor_number,
u32 sensor_mask,
int bfpi_version)
{
int i, err = 0;
for (i = 0; i < sensor_number; i++) {
if (bfpi_version == 0x03 || sensor_mask & BIT(i)) {
err = device_create_file(&pdev->dev, &attr[2 * i].dev_attr);
if (err)
break;
err = device_create_file(&pdev->dev, &attr[2 * i + 1].dev_attr);
if (err)
break;
}
}
return err;
}
static int
aaeon_hwmon_create_sysfs_files(struct platform_device *pdev, struct aaeon_hwmon_data *data)
{
int err;
/* register sysfs interface files */
err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
return err;
/* registe sysfs to dump sensors BFPI version */
err = device_create_file(&pdev->dev, &info_sys_nodes_atts[0].dev_attr);
if (err)
return err;
/* create temperature name and value node */
err = aaeon_hwmon_create_sub_sysfs_fs(pdev, temp_sys_nodes_atts,
data->sensors_number[SENSOR_TEMP_NUMBER],
data->temp_bitmap, data->bfpi_version);
if (err)
return err;
/* create fan name and value node */
err = aaeon_hwmon_create_sub_sysfs_fs(pdev, fan_sys_nodes_atts,
data->sensors_number[SENSOR_FAN_NUMBER],
data->fan_bitmap, data->bfpi_version);
if (err)
return err;
/* create voltage name and value node */
err = aaeon_hwmon_create_sub_sysfs_fs(pdev, voltage_sys_nodes_atts,
data->sensors_number[SENSOR_VOLTAGE_NUMBER],
data->voltage_bitmap, data->bfpi_version);
if (err)
return err;
return 0;
}
static void aaeon_hwmon_remove_sub_sysfs_fs(struct platform_device *pdev,
struct sensor_device_attribute_2 *attr,
int sensor_number,
u32 sensor_mask,
int bfpi_version)
{
int i;
for (i = 0; i < sensor_number; i++) {
if (bfpi_version == 0x03 || sensor_mask & BIT(i)) {
device_remove_file(&pdev->dev, &attr[2 * i].dev_attr);
device_remove_file(&pdev->dev, &attr[2 * i + 1].dev_attr);
}
}
}
static void
aaeon_hwmon_remove_sysfs_files(struct platform_device *pdev,
struct aaeon_hwmon_data *data)
{
/* degister sysfs interface files */
device_remove_file(&pdev->dev, &dev_attr_name);
/* degiste sysfs to dump sensors BFPI version */
device_remove_file(&pdev->dev, &info_sys_nodes_atts[0].dev_attr);
/* remove temperature name and value node */
aaeon_hwmon_remove_sub_sysfs_fs(pdev, temp_sys_nodes_atts,
data->sensors_number[SENSOR_TEMP_NUMBER],
data->temp_bitmap,
data->bfpi_version);
/* remove fan name and value node */
aaeon_hwmon_remove_sub_sysfs_fs(pdev, fan_sys_nodes_atts,
data->sensors_number[SENSOR_FAN_NUMBER],
data->fan_bitmap,
data->bfpi_version);
/* remove voltage name and value node */
aaeon_hwmon_remove_sub_sysfs_fs(pdev, voltage_sys_nodes_atts,
data->sensors_number[SENSOR_VOLTAGE_NUMBER],
data->voltage_bitmap,
data->bfpi_version);
}
static int aaeon_hwmon_remove(struct platform_device *pdev)
{
struct aaeon_hwmon_data *data = platform_get_drvdata(pdev);
if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev);
aaeon_hwmon_remove_sysfs_files(pdev, data);
return 0;
}
static int aaeon_get_version(void)
{
int err, retval;
u32 dev_id = 0x00;
err = asus_wmi_evaluate_method(AAEON_VERSION_METHOD_ID, dev_id, 0,
&retval);
if (err)
return err;
return retval;
}
static int aaeon_hwmon_init_drv_data(struct aaeon_hwmon_data *data)
{
int err;
data->bfpi_version = aaeon_get_version();
if (data->bfpi_version < 0) {
pr_debug("Error BFPI verion\n");
return -1;
}
if (data->bfpi_version == 0x03) {
/* set the number of bits in temp bitmap */
data->sensors_number[SENSOR_TEMP_NUMBER] =
ARRAY_SIZE(temp_sensors_name_table_V3);
data->temp_names = temp_sensors_name_table_V3;
/* set the number of bits in fan bitmap */
data->sensors_number[SENSOR_FAN_NUMBER] =
ARRAY_SIZE(fan_sensors_name_table_V3);
data->fan_names = fan_sensors_name_table_V3;
/* set the number of bits in voltage bitmap */
data->sensors_number[SENSOR_VOLTAGE_NUMBER] =
ARRAY_SIZE(voltage_sensors_name_table_V3);
data->voltage_names = voltage_sensors_name_table_V3;
} else {
/* set the number of bits in temp bitmap */
data->sensors_number[SENSOR_TEMP_NUMBER] =
ARRAY_SIZE(temp_sensors_name_table);
data->temp_names = temp_sensors_name_table;
/* set the number of bits in fan bitmap */
data->sensors_number[SENSOR_FAN_NUMBER] =
ARRAY_SIZE(fan_sensors_name_table);
data->fan_names = fan_sensors_name_table;
/* set the number of bits in voltage bitmap */
data->sensors_number[SENSOR_VOLTAGE_NUMBER] =
ARRAY_SIZE(voltage_sensors_name_table);
data->voltage_names = voltage_sensors_name_table;
}
/* get temp supported bitmap */
err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID,
BITMAP_TEMP_ARG, 0, &data->temp_bitmap);
if (err)
return err;
/* get fan supported bitmap */
err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID,
BITMAP_FAN_ARG, 0, &data->fan_bitmap);
if (err)
return err;
/* get voltage supported bitmap */
err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID,
BITMAP_VOLTAGE_ARG, 0, &data->voltage_bitmap);
if (err)
return err;
return 0;
}
static int aaeon_hwmon_probe(struct platform_device *pdev)
{
int err;
struct aaeon_hwmon_data *data;
pr_debug("aaeon hwomon device probe (support V3)!\n");
if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) {
pr_info("AAEON Management GUID not found\n");
return -ENODEV;
}
data = devm_kzalloc(&pdev->dev, sizeof(struct aaeon_hwmon_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
err = aaeon_hwmon_init_drv_data(data);
if (err) {
pr_info("Error to get sensor support bitmap\n");
goto exit;
}
if (data->bfpi_version != 0x03 && data->temp_bitmap == 0 &&
data->fan_bitmap == 0 && data->voltage_bitmap == 0) {
pr_debug("No sensors found\n");
err = -ENODEV;
goto exit;
}
platform_set_drvdata(pdev, data);
err = aaeon_hwmon_create_sysfs_files(pdev, data);
if (err)
goto exit;
data->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"AAEON_HWM",
data,
NULL,
NULL);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
data->hwmon_dev = NULL;
goto exit_unregister_sysfs;
}
return 0;
exit_unregister_sysfs:
aaeon_hwmon_remove(pdev);
exit:
return err;
}
static struct platform_driver aaeon_hwmon_driver = {
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
.probe = aaeon_hwmon_probe,
.remove = aaeon_hwmon_remove,
};
module_platform_driver_probe(aaeon_hwmon_driver, aaeon_hwmon_probe);
MODULE_ALIAS("platform:hwmon-aaeon");
MODULE_DESCRIPTION("AAEON Hardware Monitoring Driver");
MODULE_AUTHOR("Edward Lin <edward1_lin@aaeon.com.tw>");
MODULE_AUTHOR("Kunyang Fan <kunyang_fan@aaeon.com.tw>");
MODULE_LICENSE("GPL v2");