// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2007-2009 Luca Tettamanti <kronos.it@gmail.com> * * See COPYING in the top level directory of the kernel tree.
*/
staticbool new_if;
module_param(new_if, bool, 0);
MODULE_PARM_DESC(new_if, "Override detection heuristic and force the use of the new ATK0110 interface");
staticconststruct dmi_system_id __initconst atk_force_new_if[] = {
{ /* Old interface has broken MCH temp monitoring */
.ident = "Asus Sabertooth X58",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "SABERTOOTH X58")
}
}, { /* Old interface reads the same sensor for fan0 and fan1 */
.ident = "Asus M5A78L",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "M5A78L")
}
},
{ }
};
/* * Minimum time between readings, enforced in order to avoid * hogging the CPU.
*/ #define CACHE_TIME HZ
/* old interface */
acpi_handle rtmp_handle;
acpi_handle rvlt_handle;
acpi_handle rfan_handle; /* new interface */
acpi_handle enumerate_handle;
acpi_handle read_handle;
acpi_handle write_handle;
bool disable_ec;
int voltage_count; int temperature_count; int fan_count; struct list_head sensor_list; struct attribute_group attr_group; conststruct attribute_group *attr_groups[2];
/* * New package format is: * - flag (int) * class - used for de-muxing the request to the correct GITn * type (volt, temp, fan) * sensor id | * sensor id - used for de-muxing the request _inside_ the GITn * - name (str) * - unknown (int) * - unknown (int) * - limit1 (int) * - limit2 (int) * - enable (int) * * The old package has the same format but it's missing the two unknown fields.
*/ staticint validate_hwmon_pack(struct atk_data *data, union acpi_object *obj)
{ struct device *dev = &data->acpi_dev->dev; union acpi_object *tmp; bool old_if = data->old_interface; intconst expected_size = old_if ? _HWMON_OLD_PACK_SIZE :
_HWMON_NEW_PACK_SIZE;
switch (type) { case HWMON_TYPE_VOLT:
what = "voltage"; break; case HWMON_TYPE_TEMP:
what = "temperature"; break; case HWMON_TYPE_FAN:
what = "fan"; break; default:
what = "unknown"; break;
}
return what;
} #endif
staticvoid atk_print_sensor(struct atk_data *data, union acpi_object *obj)
{ #ifdef DEBUG struct device *dev = &data->acpi_dev->dev; union acpi_object *flags; union acpi_object *name; union acpi_object *limit1; union acpi_object *limit2; union acpi_object *enable; charconst *what;
buf.length = ACPI_ALLOCATE_BUFFER;
ret = acpi_evaluate_object(data->enumerate_handle, NULL, ¶ms, &buf); if (ret != AE_OK) {
dev_err(dev, "GGRP[%#x] ACPI exception: %s\n", mux,
acpi_format_exception(ret)); return ERR_PTR(-EIO);
} pack = buf.pointer; if (pack->type != ACPI_TYPE_PACKAGE) { /* Execution was successful, but the id was not found */
ACPI_FREE(pack); return ERR_PTR(-ENOENT);
}
if (pack->package.count < 1) {
dev_err(dev, "GGRP[%#x] package is too small\n", mux);
ACPI_FREE(pack); return ERR_PTR(-EIO);
} returnpack;
}
staticint atk_add_sensor(struct atk_data *data, union acpi_object *obj)
{ struct device *dev = &data->acpi_dev->dev; union acpi_object *flags; union acpi_object *name; union acpi_object *limit1; union acpi_object *limit2; union acpi_object *enable; struct atk_sensor_data *sensor; charconst *base_name; charconst *limit1_name; charconst *limit2_name;
u64 type; int err; int *num; int start;
if (obj->type != ACPI_TYPE_PACKAGE) { /* wft is this? */
dev_warn(dev, "Unknown type for ACPI object: (%d)\n",
obj->type); return -EINVAL;
}
err = validate_hwmon_pack(data, obj); if (err) return err;
/* Ok, we have a valid hwmon package */
type = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS)->integer.value
& ATK_TYPE_MASK;
pack = buf.pointer; for (i = 1; i < pack->package.count; i++) { union acpi_object *obj = &pack->package.elements[i];
ret = atk_add_sensor(data, obj); if (ret > 0)
count++;
}
ACPI_FREE(buf.pointer);
return count;
}
staticint atk_ec_present(struct atk_data *data)
{ struct device *dev = &data->acpi_dev->dev; union acpi_object *pack; union acpi_object *ec; int ret; int i;
pack = atk_ggrp(data, ATK_MUX_MGMT); if (IS_ERR(pack)) { if (PTR_ERR(pack) == -ENOENT) { /* The MGMT class does not exists - that's ok */
dev_dbg(dev, "Class %#llx not found\n", ATK_MUX_MGMT); return 0;
} return PTR_ERR(pack);
}
/* Search the EC */
ec = NULL; for (i = 0; i < pack->package.count; i++) { union acpi_object *obj = &pack->package.elements[i]; union acpi_object *id;
if (obj->type != ACPI_TYPE_PACKAGE) continue;
id = &obj->package.elements[0]; if (id->type != ACPI_TYPE_INTEGER) continue;
obj = atk_sitm(data, &sitm); if (IS_ERR(obj)) {
dev_err(dev, "Failed to %s the EC\n", str_enable_disable(enable)); return PTR_ERR(obj);
}
ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; if (ec_ret->flags == 0) {
dev_err(dev, "Failed to %s the EC\n", str_enable_disable(enable));
err = -EIO;
} else {
dev_info(dev, "EC %s\n", str_enabled_disabled(enable));
}
ACPI_FREE(obj); return err;
}
staticint atk_enumerate_new_hwmon(struct atk_data *data)
{ struct device *dev = &data->acpi_dev->dev; union acpi_object *pack; int err; int i;
err = atk_ec_present(data); if (err < 0) return err; if (err) {
err = atk_ec_enabled(data); if (err < 0) return err; /* If the EC was disabled we will disable it again on unload */
data->disable_ec = err;
/* RTMP: read temperature */
status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_TMP, &ret); if (ACPI_SUCCESS(status))
data->rtmp_handle = ret; else
dev_dbg(dev, "method " METHOD_OLD_READ_TMP " not found: %s\n",
acpi_format_exception(status));
/* RVLT: read voltage */
status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_VLT, &ret); if (ACPI_SUCCESS(status))
data->rvlt_handle = ret; else
dev_dbg(dev, "method " METHOD_OLD_READ_VLT " not found: %s\n",
acpi_format_exception(status));
/* RFAN: read fan status */
status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_FAN, &ret); if (ACPI_SUCCESS(status))
data->rfan_handle = ret; else
dev_dbg(dev, "method " METHOD_OLD_READ_FAN " not found: %s\n",
acpi_format_exception(status));
/* Enumeration */
status = acpi_get_handle(data->atk_handle, METHOD_ENUMERATE, &ret); if (ACPI_SUCCESS(status))
data->enumerate_handle = ret; else
dev_dbg(dev, "method " METHOD_ENUMERATE " not found: %s\n",
acpi_format_exception(status));
/* De-multiplexer (read) */
status = acpi_get_handle(data->atk_handle, METHOD_READ, &ret); if (ACPI_SUCCESS(status))
data->read_handle = ret; else
dev_dbg(dev, "method " METHOD_READ " not found: %s\n",
acpi_format_exception(status));
/* De-multiplexer (write) */
status = acpi_get_handle(data->atk_handle, METHOD_WRITE, &ret); if (ACPI_SUCCESS(status))
data->write_handle = ret; else
dev_dbg(dev, "method " METHOD_WRITE " not found: %s\n",
acpi_format_exception(status));
/* * Check for hwmon methods: first check "old" style methods; note that * both may be present: in this case we stick to the old interface; * analysis of multiple DSDTs indicates that when both interfaces * are present the new one (GGRP/GITM) is not functional.
*/ if (new_if)
dev_info(dev, "Overriding interface detection\n"); if (data->rtmp_handle &&
data->rvlt_handle && data->rfan_handle && !new_if)
data->old_interface = true; elseif (data->enumerate_handle && data->read_handle &&
data->write_handle)
data->old_interface = false; else
err = -ENODEV;
return err;
}
staticint atk_add(struct acpi_device *device)
{
acpi_status ret; int err; struct acpi_buffer buf; union acpi_object *obj; struct atk_data *data;
dev_dbg(&device->dev, "adding...\n");
data = devm_kzalloc(&device->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
if (data->disable_ec) { if (atk_ec_ctl(data, 0))
dev_err(&device->dev, "Failed to disable EC\n");
}
}
staticint __init atk0110_init(void)
{ int ret;
/* Make sure it's safe to access the device through ACPI */ if (!acpi_resources_are_enforced()) {
pr_err("Resources not safely usable due to acpi_enforce_resources kernel parameter\n"); return -EBUSY;
}
if (dmi_check_system(atk_force_new_if))
new_if = true;
ret = acpi_bus_register_driver(&atk_driver); if (ret)
pr_info("acpi_bus_register_driver failed: %d\n", ret);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.