12c03d07aSLuca Tettamanti /* 22c03d07aSLuca Tettamanti * Copyright (C) 2007-2009 Luca Tettamanti <kronos.it@gmail.com> 32c03d07aSLuca Tettamanti * 42c03d07aSLuca Tettamanti * This file is released under the GPLv2 52c03d07aSLuca Tettamanti * See COPYING in the top level directory of the kernel tree. 62c03d07aSLuca Tettamanti */ 72c03d07aSLuca Tettamanti 82c03d07aSLuca Tettamanti #include <linux/kernel.h> 92c03d07aSLuca Tettamanti #include <linux/hwmon.h> 102c03d07aSLuca Tettamanti #include <linux/list.h> 112c03d07aSLuca Tettamanti #include <linux/module.h> 122c03d07aSLuca Tettamanti 132c03d07aSLuca Tettamanti #include <acpi/acpi.h> 142c03d07aSLuca Tettamanti #include <acpi/acpixf.h> 152c03d07aSLuca Tettamanti #include <acpi/acpi_drivers.h> 162c03d07aSLuca Tettamanti #include <acpi/acpi_bus.h> 172c03d07aSLuca Tettamanti 182c03d07aSLuca Tettamanti 192c03d07aSLuca Tettamanti #define ATK_HID "ATK0110" 202c03d07aSLuca Tettamanti 212c03d07aSLuca Tettamanti /* Minimum time between readings, enforced in order to avoid 222c03d07aSLuca Tettamanti * hogging the CPU. 232c03d07aSLuca Tettamanti */ 242c03d07aSLuca Tettamanti #define CACHE_TIME HZ 252c03d07aSLuca Tettamanti 262c03d07aSLuca Tettamanti #define BOARD_ID "MBIF" 272c03d07aSLuca Tettamanti #define METHOD_ENUMERATE "GGRP" 282c03d07aSLuca Tettamanti #define METHOD_READ "GITM" 292c03d07aSLuca Tettamanti #define METHOD_WRITE "SITM" 302c03d07aSLuca Tettamanti #define METHOD_OLD_READ_TMP "RTMP" 312c03d07aSLuca Tettamanti #define METHOD_OLD_READ_VLT "RVLT" 322c03d07aSLuca Tettamanti #define METHOD_OLD_READ_FAN "RFAN" 332c03d07aSLuca Tettamanti #define METHOD_OLD_ENUM_TMP "TSIF" 342c03d07aSLuca Tettamanti #define METHOD_OLD_ENUM_VLT "VSIF" 352c03d07aSLuca Tettamanti #define METHOD_OLD_ENUM_FAN "FSIF" 362c03d07aSLuca Tettamanti 372c03d07aSLuca Tettamanti #define ATK_MUX_HWMON 0x00000006ULL 382c03d07aSLuca Tettamanti 392c03d07aSLuca Tettamanti #define ATK_CLASS_MASK 0xff000000ULL 402c03d07aSLuca Tettamanti #define ATK_CLASS_FREQ_CTL 0x03000000ULL 412c03d07aSLuca Tettamanti #define ATK_CLASS_FAN_CTL 0x04000000ULL 422c03d07aSLuca Tettamanti #define ATK_CLASS_HWMON 0x06000000ULL 432c03d07aSLuca Tettamanti 442c03d07aSLuca Tettamanti #define ATK_TYPE_MASK 0x00ff0000ULL 452c03d07aSLuca Tettamanti #define HWMON_TYPE_VOLT 0x00020000ULL 462c03d07aSLuca Tettamanti #define HWMON_TYPE_TEMP 0x00030000ULL 472c03d07aSLuca Tettamanti #define HWMON_TYPE_FAN 0x00040000ULL 482c03d07aSLuca Tettamanti 492c03d07aSLuca Tettamanti #define HWMON_SENSOR_ID_MASK 0x0000ffffULL 502c03d07aSLuca Tettamanti 512c03d07aSLuca Tettamanti enum atk_pack_member { 522c03d07aSLuca Tettamanti HWMON_PACK_FLAGS, 532c03d07aSLuca Tettamanti HWMON_PACK_NAME, 542c03d07aSLuca Tettamanti HWMON_PACK_LIMIT1, 552c03d07aSLuca Tettamanti HWMON_PACK_LIMIT2, 562c03d07aSLuca Tettamanti HWMON_PACK_ENABLE 572c03d07aSLuca Tettamanti }; 582c03d07aSLuca Tettamanti 592c03d07aSLuca Tettamanti /* New package format */ 602c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_SIZE 7 612c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_FLAGS 0 622c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_NAME 1 632c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_UNK1 2 642c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_UNK2 3 652c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_LIMIT1 4 662c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_LIMIT2 5 672c03d07aSLuca Tettamanti #define _HWMON_NEW_PACK_ENABLE 6 682c03d07aSLuca Tettamanti 692c03d07aSLuca Tettamanti /* Old package format */ 702c03d07aSLuca Tettamanti #define _HWMON_OLD_PACK_SIZE 5 712c03d07aSLuca Tettamanti #define _HWMON_OLD_PACK_FLAGS 0 722c03d07aSLuca Tettamanti #define _HWMON_OLD_PACK_NAME 1 732c03d07aSLuca Tettamanti #define _HWMON_OLD_PACK_LIMIT1 2 742c03d07aSLuca Tettamanti #define _HWMON_OLD_PACK_LIMIT2 3 752c03d07aSLuca Tettamanti #define _HWMON_OLD_PACK_ENABLE 4 762c03d07aSLuca Tettamanti 772c03d07aSLuca Tettamanti 782c03d07aSLuca Tettamanti struct atk_data { 792c03d07aSLuca Tettamanti struct device *hwmon_dev; 802c03d07aSLuca Tettamanti acpi_handle atk_handle; 812c03d07aSLuca Tettamanti struct acpi_device *acpi_dev; 822c03d07aSLuca Tettamanti 832c03d07aSLuca Tettamanti bool old_interface; 842c03d07aSLuca Tettamanti 852c03d07aSLuca Tettamanti /* old interface */ 862c03d07aSLuca Tettamanti acpi_handle rtmp_handle; 872c03d07aSLuca Tettamanti acpi_handle rvlt_handle; 882c03d07aSLuca Tettamanti acpi_handle rfan_handle; 892c03d07aSLuca Tettamanti /* new inteface */ 902c03d07aSLuca Tettamanti acpi_handle enumerate_handle; 912c03d07aSLuca Tettamanti acpi_handle read_handle; 922c03d07aSLuca Tettamanti 932c03d07aSLuca Tettamanti int voltage_count; 942c03d07aSLuca Tettamanti int temperature_count; 952c03d07aSLuca Tettamanti int fan_count; 962c03d07aSLuca Tettamanti struct list_head sensor_list; 972c03d07aSLuca Tettamanti }; 982c03d07aSLuca Tettamanti 992c03d07aSLuca Tettamanti 1002c03d07aSLuca Tettamanti typedef ssize_t (*sysfs_show_func)(struct device *dev, 1012c03d07aSLuca Tettamanti struct device_attribute *attr, char *buf); 1022c03d07aSLuca Tettamanti 1032c03d07aSLuca Tettamanti static const struct acpi_device_id atk_ids[] = { 1042c03d07aSLuca Tettamanti {ATK_HID, 0}, 1052c03d07aSLuca Tettamanti {"", 0}, 1062c03d07aSLuca Tettamanti }; 1072c03d07aSLuca Tettamanti MODULE_DEVICE_TABLE(acpi, atk_ids); 1082c03d07aSLuca Tettamanti 1092c03d07aSLuca Tettamanti #define ATTR_NAME_SIZE 16 /* Worst case is "tempN_input" */ 1102c03d07aSLuca Tettamanti 1112c03d07aSLuca Tettamanti struct atk_sensor_data { 1122c03d07aSLuca Tettamanti struct list_head list; 1132c03d07aSLuca Tettamanti struct atk_data *data; 1142c03d07aSLuca Tettamanti struct device_attribute label_attr; 1152c03d07aSLuca Tettamanti struct device_attribute input_attr; 1162c03d07aSLuca Tettamanti struct device_attribute limit1_attr; 1172c03d07aSLuca Tettamanti struct device_attribute limit2_attr; 1182c03d07aSLuca Tettamanti char label_attr_name[ATTR_NAME_SIZE]; 1192c03d07aSLuca Tettamanti char input_attr_name[ATTR_NAME_SIZE]; 1202c03d07aSLuca Tettamanti char limit1_attr_name[ATTR_NAME_SIZE]; 1212c03d07aSLuca Tettamanti char limit2_attr_name[ATTR_NAME_SIZE]; 1222c03d07aSLuca Tettamanti u64 id; 1232c03d07aSLuca Tettamanti u64 type; 1242c03d07aSLuca Tettamanti u64 limit1; 1252c03d07aSLuca Tettamanti u64 limit2; 1262c03d07aSLuca Tettamanti u64 cached_value; 1272c03d07aSLuca Tettamanti unsigned long last_updated; /* in jiffies */ 1282c03d07aSLuca Tettamanti bool is_valid; 1292c03d07aSLuca Tettamanti char const *acpi_name; 1302c03d07aSLuca Tettamanti }; 1312c03d07aSLuca Tettamanti 1322c03d07aSLuca Tettamanti struct atk_acpi_buffer_u64 { 1332c03d07aSLuca Tettamanti union acpi_object buf; 1342c03d07aSLuca Tettamanti u64 value; 1352c03d07aSLuca Tettamanti }; 1362c03d07aSLuca Tettamanti 1372c03d07aSLuca Tettamanti static int atk_add(struct acpi_device *device); 1382c03d07aSLuca Tettamanti static int atk_remove(struct acpi_device *device, int type); 1392c03d07aSLuca Tettamanti static void atk_print_sensor(struct atk_data *data, union acpi_object *obj); 1402c03d07aSLuca Tettamanti static int atk_read_value(struct atk_sensor_data *sensor, u64 *value); 1412c03d07aSLuca Tettamanti static void atk_free_sensors(struct atk_data *data); 1422c03d07aSLuca Tettamanti 1432c03d07aSLuca Tettamanti static struct acpi_driver atk_driver = { 1442c03d07aSLuca Tettamanti .name = ATK_HID, 1452c03d07aSLuca Tettamanti .class = "hwmon", 1462c03d07aSLuca Tettamanti .ids = atk_ids, 1472c03d07aSLuca Tettamanti .ops = { 1482c03d07aSLuca Tettamanti .add = atk_add, 1492c03d07aSLuca Tettamanti .remove = atk_remove, 1502c03d07aSLuca Tettamanti }, 1512c03d07aSLuca Tettamanti }; 1522c03d07aSLuca Tettamanti 1532c03d07aSLuca Tettamanti #define input_to_atk_sensor(attr) \ 1542c03d07aSLuca Tettamanti container_of(attr, struct atk_sensor_data, input_attr) 1552c03d07aSLuca Tettamanti 1562c03d07aSLuca Tettamanti #define label_to_atk_sensor(attr) \ 1572c03d07aSLuca Tettamanti container_of(attr, struct atk_sensor_data, label_attr) 1582c03d07aSLuca Tettamanti 1592c03d07aSLuca Tettamanti #define limit1_to_atk_sensor(attr) \ 1602c03d07aSLuca Tettamanti container_of(attr, struct atk_sensor_data, limit1_attr) 1612c03d07aSLuca Tettamanti 1622c03d07aSLuca Tettamanti #define limit2_to_atk_sensor(attr) \ 1632c03d07aSLuca Tettamanti container_of(attr, struct atk_sensor_data, limit2_attr) 1642c03d07aSLuca Tettamanti 1652c03d07aSLuca Tettamanti static ssize_t atk_input_show(struct device *dev, 1662c03d07aSLuca Tettamanti struct device_attribute *attr, char *buf) 1672c03d07aSLuca Tettamanti { 1682c03d07aSLuca Tettamanti struct atk_sensor_data *s = input_to_atk_sensor(attr); 1692c03d07aSLuca Tettamanti u64 value; 1702c03d07aSLuca Tettamanti int err; 1712c03d07aSLuca Tettamanti 1722c03d07aSLuca Tettamanti err = atk_read_value(s, &value); 1732c03d07aSLuca Tettamanti if (err) 1742c03d07aSLuca Tettamanti return err; 1752c03d07aSLuca Tettamanti 1762c03d07aSLuca Tettamanti if (s->type == HWMON_TYPE_TEMP) 1772c03d07aSLuca Tettamanti /* ACPI returns decidegree */ 1782c03d07aSLuca Tettamanti value *= 100; 1792c03d07aSLuca Tettamanti 1802c03d07aSLuca Tettamanti return sprintf(buf, "%llu\n", value); 1812c03d07aSLuca Tettamanti } 1822c03d07aSLuca Tettamanti 1832c03d07aSLuca Tettamanti static ssize_t atk_label_show(struct device *dev, 1842c03d07aSLuca Tettamanti struct device_attribute *attr, char *buf) 1852c03d07aSLuca Tettamanti { 1862c03d07aSLuca Tettamanti struct atk_sensor_data *s = label_to_atk_sensor(attr); 1872c03d07aSLuca Tettamanti 1882c03d07aSLuca Tettamanti return sprintf(buf, "%s\n", s->acpi_name); 1892c03d07aSLuca Tettamanti } 1902c03d07aSLuca Tettamanti 1912c03d07aSLuca Tettamanti static ssize_t atk_limit1_show(struct device *dev, 1922c03d07aSLuca Tettamanti struct device_attribute *attr, char *buf) 1932c03d07aSLuca Tettamanti { 1942c03d07aSLuca Tettamanti struct atk_sensor_data *s = limit1_to_atk_sensor(attr); 1952c03d07aSLuca Tettamanti u64 value = s->limit1; 1962c03d07aSLuca Tettamanti 1972c03d07aSLuca Tettamanti if (s->type == HWMON_TYPE_TEMP) 1982c03d07aSLuca Tettamanti value *= 100; 1992c03d07aSLuca Tettamanti 2002c03d07aSLuca Tettamanti return sprintf(buf, "%lld\n", value); 2012c03d07aSLuca Tettamanti } 2022c03d07aSLuca Tettamanti 2032c03d07aSLuca Tettamanti static ssize_t atk_limit2_show(struct device *dev, 2042c03d07aSLuca Tettamanti struct device_attribute *attr, char *buf) 2052c03d07aSLuca Tettamanti { 2062c03d07aSLuca Tettamanti struct atk_sensor_data *s = limit2_to_atk_sensor(attr); 2072c03d07aSLuca Tettamanti u64 value = s->limit2; 2082c03d07aSLuca Tettamanti 2092c03d07aSLuca Tettamanti if (s->type == HWMON_TYPE_TEMP) 2102c03d07aSLuca Tettamanti value *= 100; 2112c03d07aSLuca Tettamanti 2122c03d07aSLuca Tettamanti return sprintf(buf, "%lld\n", value); 2132c03d07aSLuca Tettamanti } 2142c03d07aSLuca Tettamanti 2152c03d07aSLuca Tettamanti static ssize_t atk_name_show(struct device *dev, 2162c03d07aSLuca Tettamanti struct device_attribute *attr, char *buf) 2172c03d07aSLuca Tettamanti { 2182c03d07aSLuca Tettamanti return sprintf(buf, "atk0110\n"); 2192c03d07aSLuca Tettamanti } 2202c03d07aSLuca Tettamanti static struct device_attribute atk_name_attr = 2212c03d07aSLuca Tettamanti __ATTR(name, 0444, atk_name_show, NULL); 2222c03d07aSLuca Tettamanti 2232c03d07aSLuca Tettamanti static void atk_init_attribute(struct device_attribute *attr, char *name, 2242c03d07aSLuca Tettamanti sysfs_show_func show) 2252c03d07aSLuca Tettamanti { 2262c03d07aSLuca Tettamanti attr->attr.name = name; 2272c03d07aSLuca Tettamanti attr->attr.mode = 0444; 2282c03d07aSLuca Tettamanti attr->show = show; 2292c03d07aSLuca Tettamanti attr->store = NULL; 2302c03d07aSLuca Tettamanti } 2312c03d07aSLuca Tettamanti 2322c03d07aSLuca Tettamanti 2332c03d07aSLuca Tettamanti static union acpi_object *atk_get_pack_member(struct atk_data *data, 2342c03d07aSLuca Tettamanti union acpi_object *pack, 2352c03d07aSLuca Tettamanti enum atk_pack_member m) 2362c03d07aSLuca Tettamanti { 2372c03d07aSLuca Tettamanti bool old_if = data->old_interface; 2382c03d07aSLuca Tettamanti int offset; 2392c03d07aSLuca Tettamanti 2402c03d07aSLuca Tettamanti switch (m) { 2412c03d07aSLuca Tettamanti case HWMON_PACK_FLAGS: 2422c03d07aSLuca Tettamanti offset = old_if ? _HWMON_OLD_PACK_FLAGS : _HWMON_NEW_PACK_FLAGS; 2432c03d07aSLuca Tettamanti break; 2442c03d07aSLuca Tettamanti case HWMON_PACK_NAME: 2452c03d07aSLuca Tettamanti offset = old_if ? _HWMON_OLD_PACK_NAME : _HWMON_NEW_PACK_NAME; 2462c03d07aSLuca Tettamanti break; 2472c03d07aSLuca Tettamanti case HWMON_PACK_LIMIT1: 2482c03d07aSLuca Tettamanti offset = old_if ? _HWMON_OLD_PACK_LIMIT1 : 2492c03d07aSLuca Tettamanti _HWMON_NEW_PACK_LIMIT1; 2502c03d07aSLuca Tettamanti break; 2512c03d07aSLuca Tettamanti case HWMON_PACK_LIMIT2: 2522c03d07aSLuca Tettamanti offset = old_if ? _HWMON_OLD_PACK_LIMIT2 : 2532c03d07aSLuca Tettamanti _HWMON_NEW_PACK_LIMIT2; 2542c03d07aSLuca Tettamanti break; 2552c03d07aSLuca Tettamanti case HWMON_PACK_ENABLE: 2562c03d07aSLuca Tettamanti offset = old_if ? _HWMON_OLD_PACK_ENABLE : 2572c03d07aSLuca Tettamanti _HWMON_NEW_PACK_ENABLE; 2582c03d07aSLuca Tettamanti break; 2592c03d07aSLuca Tettamanti default: 2602c03d07aSLuca Tettamanti return NULL; 2612c03d07aSLuca Tettamanti } 2622c03d07aSLuca Tettamanti 2632c03d07aSLuca Tettamanti return &pack->package.elements[offset]; 2642c03d07aSLuca Tettamanti } 2652c03d07aSLuca Tettamanti 2662c03d07aSLuca Tettamanti 2672c03d07aSLuca Tettamanti /* New package format is: 2682c03d07aSLuca Tettamanti * - flag (int) 2692c03d07aSLuca Tettamanti * class - used for de-muxing the request to the correct GITn 2702c03d07aSLuca Tettamanti * type (volt, temp, fan) 2712c03d07aSLuca Tettamanti * sensor id | 2722c03d07aSLuca Tettamanti * sensor id - used for de-muxing the request _inside_ the GITn 2732c03d07aSLuca Tettamanti * - name (str) 2742c03d07aSLuca Tettamanti * - unknown (int) 2752c03d07aSLuca Tettamanti * - unknown (int) 2762c03d07aSLuca Tettamanti * - limit1 (int) 2772c03d07aSLuca Tettamanti * - limit2 (int) 2782c03d07aSLuca Tettamanti * - enable (int) 2792c03d07aSLuca Tettamanti * 2802c03d07aSLuca Tettamanti * The old package has the same format but it's missing the two unknown fields. 2812c03d07aSLuca Tettamanti */ 2822c03d07aSLuca Tettamanti static int validate_hwmon_pack(struct atk_data *data, union acpi_object *obj) 2832c03d07aSLuca Tettamanti { 2842c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 2852c03d07aSLuca Tettamanti union acpi_object *tmp; 2862c03d07aSLuca Tettamanti bool old_if = data->old_interface; 2872c03d07aSLuca Tettamanti int const expected_size = old_if ? _HWMON_OLD_PACK_SIZE : 2882c03d07aSLuca Tettamanti _HWMON_NEW_PACK_SIZE; 2892c03d07aSLuca Tettamanti 2902c03d07aSLuca Tettamanti if (obj->type != ACPI_TYPE_PACKAGE) { 2912c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type: %d\n", obj->type); 2922c03d07aSLuca Tettamanti return -EINVAL; 2932c03d07aSLuca Tettamanti } 2942c03d07aSLuca Tettamanti 2952c03d07aSLuca Tettamanti if (obj->package.count != expected_size) { 2962c03d07aSLuca Tettamanti dev_warn(dev, "Invalid package size: %d, expected: %d\n", 2972c03d07aSLuca Tettamanti obj->package.count, expected_size); 2982c03d07aSLuca Tettamanti return -EINVAL; 2992c03d07aSLuca Tettamanti } 3002c03d07aSLuca Tettamanti 3012c03d07aSLuca Tettamanti tmp = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS); 3022c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_INTEGER) { 3032c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (flag): %d\n", tmp->type); 3042c03d07aSLuca Tettamanti return -EINVAL; 3052c03d07aSLuca Tettamanti } 3062c03d07aSLuca Tettamanti 3072c03d07aSLuca Tettamanti tmp = atk_get_pack_member(data, obj, HWMON_PACK_NAME); 3082c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_STRING) { 3092c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (name): %d\n", tmp->type); 3102c03d07aSLuca Tettamanti return -EINVAL; 3112c03d07aSLuca Tettamanti } 3122c03d07aSLuca Tettamanti 3132c03d07aSLuca Tettamanti /* Don't check... we don't know what they're useful for anyway */ 3142c03d07aSLuca Tettamanti #if 0 3152c03d07aSLuca Tettamanti tmp = &obj->package.elements[HWMON_PACK_UNK1]; 3162c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_INTEGER) { 3172c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (unk1): %d\n", tmp->type); 3182c03d07aSLuca Tettamanti return -EINVAL; 3192c03d07aSLuca Tettamanti } 3202c03d07aSLuca Tettamanti 3212c03d07aSLuca Tettamanti tmp = &obj->package.elements[HWMON_PACK_UNK2]; 3222c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_INTEGER) { 3232c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (unk2): %d\n", tmp->type); 3242c03d07aSLuca Tettamanti return -EINVAL; 3252c03d07aSLuca Tettamanti } 3262c03d07aSLuca Tettamanti #endif 3272c03d07aSLuca Tettamanti 3282c03d07aSLuca Tettamanti tmp = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT1); 3292c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_INTEGER) { 3302c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (limit1): %d\n", tmp->type); 3312c03d07aSLuca Tettamanti return -EINVAL; 3322c03d07aSLuca Tettamanti } 3332c03d07aSLuca Tettamanti 3342c03d07aSLuca Tettamanti tmp = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT2); 3352c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_INTEGER) { 3362c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (limit2): %d\n", tmp->type); 3372c03d07aSLuca Tettamanti return -EINVAL; 3382c03d07aSLuca Tettamanti } 3392c03d07aSLuca Tettamanti 3402c03d07aSLuca Tettamanti tmp = atk_get_pack_member(data, obj, HWMON_PACK_ENABLE); 3412c03d07aSLuca Tettamanti if (tmp->type != ACPI_TYPE_INTEGER) { 3422c03d07aSLuca Tettamanti dev_warn(dev, "Invalid type (enable): %d\n", tmp->type); 3432c03d07aSLuca Tettamanti return -EINVAL; 3442c03d07aSLuca Tettamanti } 3452c03d07aSLuca Tettamanti 3462c03d07aSLuca Tettamanti atk_print_sensor(data, obj); 3472c03d07aSLuca Tettamanti 3482c03d07aSLuca Tettamanti return 0; 3492c03d07aSLuca Tettamanti } 3502c03d07aSLuca Tettamanti 351*b9008708SLuca Tettamanti #ifdef DEBUG 3522c03d07aSLuca Tettamanti static char const *atk_sensor_type(union acpi_object *flags) 3532c03d07aSLuca Tettamanti { 3542c03d07aSLuca Tettamanti u64 type = flags->integer.value & ATK_TYPE_MASK; 3552c03d07aSLuca Tettamanti char const *what; 3562c03d07aSLuca Tettamanti 3572c03d07aSLuca Tettamanti switch (type) { 3582c03d07aSLuca Tettamanti case HWMON_TYPE_VOLT: 3592c03d07aSLuca Tettamanti what = "voltage"; 3602c03d07aSLuca Tettamanti break; 3612c03d07aSLuca Tettamanti case HWMON_TYPE_TEMP: 3622c03d07aSLuca Tettamanti what = "temperature"; 3632c03d07aSLuca Tettamanti break; 3642c03d07aSLuca Tettamanti case HWMON_TYPE_FAN: 3652c03d07aSLuca Tettamanti what = "fan"; 3662c03d07aSLuca Tettamanti break; 3672c03d07aSLuca Tettamanti default: 3682c03d07aSLuca Tettamanti what = "unknown"; 3692c03d07aSLuca Tettamanti break; 3702c03d07aSLuca Tettamanti } 3712c03d07aSLuca Tettamanti 3722c03d07aSLuca Tettamanti return what; 3732c03d07aSLuca Tettamanti } 374*b9008708SLuca Tettamanti #endif 3752c03d07aSLuca Tettamanti 3762c03d07aSLuca Tettamanti static void atk_print_sensor(struct atk_data *data, union acpi_object *obj) 3772c03d07aSLuca Tettamanti { 3782c03d07aSLuca Tettamanti #ifdef DEBUG 3792c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 3802c03d07aSLuca Tettamanti union acpi_object *flags; 3812c03d07aSLuca Tettamanti union acpi_object *name; 3822c03d07aSLuca Tettamanti union acpi_object *limit1; 3832c03d07aSLuca Tettamanti union acpi_object *limit2; 3842c03d07aSLuca Tettamanti union acpi_object *enable; 3852c03d07aSLuca Tettamanti char const *what; 3862c03d07aSLuca Tettamanti 3872c03d07aSLuca Tettamanti flags = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS); 3882c03d07aSLuca Tettamanti name = atk_get_pack_member(data, obj, HWMON_PACK_NAME); 3892c03d07aSLuca Tettamanti limit1 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT1); 3902c03d07aSLuca Tettamanti limit2 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT2); 3912c03d07aSLuca Tettamanti enable = atk_get_pack_member(data, obj, HWMON_PACK_ENABLE); 3922c03d07aSLuca Tettamanti 3932c03d07aSLuca Tettamanti what = atk_sensor_type(flags); 3942c03d07aSLuca Tettamanti 3952c03d07aSLuca Tettamanti dev_dbg(dev, "%s: %#llx %s [%llu-%llu] %s\n", what, 3962c03d07aSLuca Tettamanti flags->integer.value, 3972c03d07aSLuca Tettamanti name->string.pointer, 3982c03d07aSLuca Tettamanti limit1->integer.value, limit2->integer.value, 3992c03d07aSLuca Tettamanti enable->integer.value ? "enabled" : "disabled"); 4002c03d07aSLuca Tettamanti #endif 4012c03d07aSLuca Tettamanti } 4022c03d07aSLuca Tettamanti 4032c03d07aSLuca Tettamanti static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value) 4042c03d07aSLuca Tettamanti { 4052c03d07aSLuca Tettamanti struct atk_data *data = sensor->data; 4062c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 4072c03d07aSLuca Tettamanti struct acpi_object_list params; 4082c03d07aSLuca Tettamanti union acpi_object id; 4092c03d07aSLuca Tettamanti acpi_status status; 4102c03d07aSLuca Tettamanti acpi_handle method; 4112c03d07aSLuca Tettamanti 4122c03d07aSLuca Tettamanti switch (sensor->type) { 4132c03d07aSLuca Tettamanti case HWMON_TYPE_VOLT: 4142c03d07aSLuca Tettamanti method = data->rvlt_handle; 4152c03d07aSLuca Tettamanti break; 4162c03d07aSLuca Tettamanti case HWMON_TYPE_TEMP: 4172c03d07aSLuca Tettamanti method = data->rtmp_handle; 4182c03d07aSLuca Tettamanti break; 4192c03d07aSLuca Tettamanti case HWMON_TYPE_FAN: 4202c03d07aSLuca Tettamanti method = data->rfan_handle; 4212c03d07aSLuca Tettamanti break; 4222c03d07aSLuca Tettamanti default: 4232c03d07aSLuca Tettamanti return -EINVAL; 4242c03d07aSLuca Tettamanti } 4252c03d07aSLuca Tettamanti 4262c03d07aSLuca Tettamanti id.type = ACPI_TYPE_INTEGER; 4272c03d07aSLuca Tettamanti id.integer.value = sensor->id; 4282c03d07aSLuca Tettamanti 4292c03d07aSLuca Tettamanti params.count = 1; 4302c03d07aSLuca Tettamanti params.pointer = &id; 4312c03d07aSLuca Tettamanti 4322c03d07aSLuca Tettamanti status = acpi_evaluate_integer(method, NULL, ¶ms, value); 4332c03d07aSLuca Tettamanti if (status != AE_OK) { 4342c03d07aSLuca Tettamanti dev_warn(dev, "%s: ACPI exception: %s\n", __func__, 4352c03d07aSLuca Tettamanti acpi_format_exception(status)); 4362c03d07aSLuca Tettamanti return -EIO; 4372c03d07aSLuca Tettamanti } 4382c03d07aSLuca Tettamanti 4392c03d07aSLuca Tettamanti return 0; 4402c03d07aSLuca Tettamanti } 4412c03d07aSLuca Tettamanti 4422c03d07aSLuca Tettamanti static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) 4432c03d07aSLuca Tettamanti { 4442c03d07aSLuca Tettamanti struct atk_data *data = sensor->data; 4452c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 4462c03d07aSLuca Tettamanti struct acpi_object_list params; 4472c03d07aSLuca Tettamanti struct acpi_buffer ret; 4482c03d07aSLuca Tettamanti union acpi_object id; 4492c03d07aSLuca Tettamanti struct atk_acpi_buffer_u64 tmp; 4502c03d07aSLuca Tettamanti acpi_status status; 4512c03d07aSLuca Tettamanti 4522c03d07aSLuca Tettamanti id.type = ACPI_TYPE_INTEGER; 4532c03d07aSLuca Tettamanti id.integer.value = sensor->id; 4542c03d07aSLuca Tettamanti 4552c03d07aSLuca Tettamanti params.count = 1; 4562c03d07aSLuca Tettamanti params.pointer = &id; 4572c03d07aSLuca Tettamanti 4582c03d07aSLuca Tettamanti tmp.buf.type = ACPI_TYPE_BUFFER; 4592c03d07aSLuca Tettamanti tmp.buf.buffer.pointer = (u8 *)&tmp.value; 4602c03d07aSLuca Tettamanti tmp.buf.buffer.length = sizeof(u64); 4612c03d07aSLuca Tettamanti ret.length = sizeof(tmp); 4622c03d07aSLuca Tettamanti ret.pointer = &tmp; 4632c03d07aSLuca Tettamanti 4642c03d07aSLuca Tettamanti status = acpi_evaluate_object_typed(data->read_handle, NULL, ¶ms, 4652c03d07aSLuca Tettamanti &ret, ACPI_TYPE_BUFFER); 4662c03d07aSLuca Tettamanti if (status != AE_OK) { 4672c03d07aSLuca Tettamanti dev_warn(dev, "%s: ACPI exception: %s\n", __func__, 4682c03d07aSLuca Tettamanti acpi_format_exception(status)); 4692c03d07aSLuca Tettamanti return -EIO; 4702c03d07aSLuca Tettamanti } 4712c03d07aSLuca Tettamanti 4722c03d07aSLuca Tettamanti /* Return buffer format: 4732c03d07aSLuca Tettamanti * [0-3] "value" is valid flag 4742c03d07aSLuca Tettamanti * [4-7] value 4752c03d07aSLuca Tettamanti */ 4762c03d07aSLuca Tettamanti if (!(tmp.value & 0xffffffff)) { 4772c03d07aSLuca Tettamanti /* The reading is not valid, possible causes: 4782c03d07aSLuca Tettamanti * - sensor failure 4792c03d07aSLuca Tettamanti * - enumeration was FUBAR (and we didn't notice) 4802c03d07aSLuca Tettamanti */ 4812c03d07aSLuca Tettamanti dev_info(dev, "Failure: %#llx\n", tmp.value); 4822c03d07aSLuca Tettamanti return -EIO; 4832c03d07aSLuca Tettamanti } 4842c03d07aSLuca Tettamanti 4852c03d07aSLuca Tettamanti *value = (tmp.value & 0xffffffff00000000ULL) >> 32; 4862c03d07aSLuca Tettamanti 4872c03d07aSLuca Tettamanti return 0; 4882c03d07aSLuca Tettamanti } 4892c03d07aSLuca Tettamanti 4902c03d07aSLuca Tettamanti static int atk_read_value(struct atk_sensor_data *sensor, u64 *value) 4912c03d07aSLuca Tettamanti { 4922c03d07aSLuca Tettamanti int err; 4932c03d07aSLuca Tettamanti 4942c03d07aSLuca Tettamanti if (!sensor->is_valid || 4952c03d07aSLuca Tettamanti time_after(jiffies, sensor->last_updated + CACHE_TIME)) { 4962c03d07aSLuca Tettamanti if (sensor->data->old_interface) 4972c03d07aSLuca Tettamanti err = atk_read_value_old(sensor, value); 4982c03d07aSLuca Tettamanti else 4992c03d07aSLuca Tettamanti err = atk_read_value_new(sensor, value); 5002c03d07aSLuca Tettamanti 5012c03d07aSLuca Tettamanti sensor->is_valid = true; 5022c03d07aSLuca Tettamanti sensor->last_updated = jiffies; 5032c03d07aSLuca Tettamanti sensor->cached_value = *value; 5042c03d07aSLuca Tettamanti } else { 5052c03d07aSLuca Tettamanti *value = sensor->cached_value; 5062c03d07aSLuca Tettamanti err = 0; 5072c03d07aSLuca Tettamanti } 5082c03d07aSLuca Tettamanti 5092c03d07aSLuca Tettamanti return err; 5102c03d07aSLuca Tettamanti } 5112c03d07aSLuca Tettamanti 5122c03d07aSLuca Tettamanti static int atk_add_sensor(struct atk_data *data, union acpi_object *obj) 5132c03d07aSLuca Tettamanti { 5142c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 5152c03d07aSLuca Tettamanti union acpi_object *flags; 5162c03d07aSLuca Tettamanti union acpi_object *name; 5172c03d07aSLuca Tettamanti union acpi_object *limit1; 5182c03d07aSLuca Tettamanti union acpi_object *limit2; 5192c03d07aSLuca Tettamanti union acpi_object *enable; 5202c03d07aSLuca Tettamanti struct atk_sensor_data *sensor; 5212c03d07aSLuca Tettamanti char const *base_name; 5222c03d07aSLuca Tettamanti char const *limit1_name; 5232c03d07aSLuca Tettamanti char const *limit2_name; 5242c03d07aSLuca Tettamanti u64 type; 5252c03d07aSLuca Tettamanti int err; 5262c03d07aSLuca Tettamanti int *num; 5272c03d07aSLuca Tettamanti int start; 5282c03d07aSLuca Tettamanti 5292c03d07aSLuca Tettamanti if (obj->type != ACPI_TYPE_PACKAGE) { 5302c03d07aSLuca Tettamanti /* wft is this? */ 5312c03d07aSLuca Tettamanti dev_warn(dev, "Unknown type for ACPI object: (%d)\n", 5322c03d07aSLuca Tettamanti obj->type); 5332c03d07aSLuca Tettamanti return -EINVAL; 5342c03d07aSLuca Tettamanti } 5352c03d07aSLuca Tettamanti 5362c03d07aSLuca Tettamanti err = validate_hwmon_pack(data, obj); 5372c03d07aSLuca Tettamanti if (err) 5382c03d07aSLuca Tettamanti return err; 5392c03d07aSLuca Tettamanti 5402c03d07aSLuca Tettamanti /* Ok, we have a valid hwmon package */ 5412c03d07aSLuca Tettamanti type = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS)->integer.value 5422c03d07aSLuca Tettamanti & ATK_TYPE_MASK; 5432c03d07aSLuca Tettamanti 5442c03d07aSLuca Tettamanti switch (type) { 5452c03d07aSLuca Tettamanti case HWMON_TYPE_VOLT: 5462c03d07aSLuca Tettamanti base_name = "in"; 5472c03d07aSLuca Tettamanti limit1_name = "min"; 5482c03d07aSLuca Tettamanti limit2_name = "max"; 5492c03d07aSLuca Tettamanti num = &data->voltage_count; 5502c03d07aSLuca Tettamanti start = 0; 5512c03d07aSLuca Tettamanti break; 5522c03d07aSLuca Tettamanti case HWMON_TYPE_TEMP: 5532c03d07aSLuca Tettamanti base_name = "temp"; 5542c03d07aSLuca Tettamanti limit1_name = "max"; 5552c03d07aSLuca Tettamanti limit2_name = "crit"; 5562c03d07aSLuca Tettamanti num = &data->temperature_count; 5572c03d07aSLuca Tettamanti start = 1; 5582c03d07aSLuca Tettamanti break; 5592c03d07aSLuca Tettamanti case HWMON_TYPE_FAN: 5602c03d07aSLuca Tettamanti base_name = "fan"; 5612c03d07aSLuca Tettamanti limit1_name = "min"; 5622c03d07aSLuca Tettamanti limit2_name = "max"; 5632c03d07aSLuca Tettamanti num = &data->fan_count; 5642c03d07aSLuca Tettamanti start = 1; 5652c03d07aSLuca Tettamanti break; 5662c03d07aSLuca Tettamanti default: 5672c03d07aSLuca Tettamanti dev_warn(dev, "Unknown sensor type: %#llx\n", type); 5682c03d07aSLuca Tettamanti return -EINVAL; 5692c03d07aSLuca Tettamanti } 5702c03d07aSLuca Tettamanti 5712c03d07aSLuca Tettamanti enable = atk_get_pack_member(data, obj, HWMON_PACK_ENABLE); 5722c03d07aSLuca Tettamanti if (!enable->integer.value) 5732c03d07aSLuca Tettamanti /* sensor is disabled */ 5742c03d07aSLuca Tettamanti return 0; 5752c03d07aSLuca Tettamanti 5762c03d07aSLuca Tettamanti flags = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS); 5772c03d07aSLuca Tettamanti name = atk_get_pack_member(data, obj, HWMON_PACK_NAME); 5782c03d07aSLuca Tettamanti limit1 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT1); 5792c03d07aSLuca Tettamanti limit2 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT2); 5802c03d07aSLuca Tettamanti 5812c03d07aSLuca Tettamanti sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); 5822c03d07aSLuca Tettamanti if (!sensor) 5832c03d07aSLuca Tettamanti return -ENOMEM; 5842c03d07aSLuca Tettamanti 5852c03d07aSLuca Tettamanti sensor->acpi_name = kstrdup(name->string.pointer, GFP_KERNEL); 5862c03d07aSLuca Tettamanti if (!sensor->acpi_name) { 5872c03d07aSLuca Tettamanti err = -ENOMEM; 5882c03d07aSLuca Tettamanti goto out; 5892c03d07aSLuca Tettamanti } 5902c03d07aSLuca Tettamanti 5912c03d07aSLuca Tettamanti INIT_LIST_HEAD(&sensor->list); 5922c03d07aSLuca Tettamanti sensor->type = type; 5932c03d07aSLuca Tettamanti sensor->data = data; 5942c03d07aSLuca Tettamanti sensor->id = flags->integer.value; 5952c03d07aSLuca Tettamanti sensor->limit1 = limit1->integer.value; 5962c03d07aSLuca Tettamanti sensor->limit2 = limit2->integer.value; 5972c03d07aSLuca Tettamanti 5982c03d07aSLuca Tettamanti snprintf(sensor->input_attr_name, ATTR_NAME_SIZE, 5992c03d07aSLuca Tettamanti "%s%d_input", base_name, start + *num); 6002c03d07aSLuca Tettamanti atk_init_attribute(&sensor->input_attr, 6012c03d07aSLuca Tettamanti sensor->input_attr_name, 6022c03d07aSLuca Tettamanti atk_input_show); 6032c03d07aSLuca Tettamanti 6042c03d07aSLuca Tettamanti snprintf(sensor->label_attr_name, ATTR_NAME_SIZE, 6052c03d07aSLuca Tettamanti "%s%d_label", base_name, start + *num); 6062c03d07aSLuca Tettamanti atk_init_attribute(&sensor->label_attr, 6072c03d07aSLuca Tettamanti sensor->label_attr_name, 6082c03d07aSLuca Tettamanti atk_label_show); 6092c03d07aSLuca Tettamanti 6102c03d07aSLuca Tettamanti snprintf(sensor->limit1_attr_name, ATTR_NAME_SIZE, 6112c03d07aSLuca Tettamanti "%s%d_%s", base_name, start + *num, limit1_name); 6122c03d07aSLuca Tettamanti atk_init_attribute(&sensor->limit1_attr, 6132c03d07aSLuca Tettamanti sensor->limit1_attr_name, 6142c03d07aSLuca Tettamanti atk_limit1_show); 6152c03d07aSLuca Tettamanti 6162c03d07aSLuca Tettamanti snprintf(sensor->limit2_attr_name, ATTR_NAME_SIZE, 6172c03d07aSLuca Tettamanti "%s%d_%s", base_name, start + *num, limit2_name); 6182c03d07aSLuca Tettamanti atk_init_attribute(&sensor->limit2_attr, 6192c03d07aSLuca Tettamanti sensor->limit2_attr_name, 6202c03d07aSLuca Tettamanti atk_limit2_show); 6212c03d07aSLuca Tettamanti 6222c03d07aSLuca Tettamanti list_add(&sensor->list, &data->sensor_list); 6232c03d07aSLuca Tettamanti (*num)++; 6242c03d07aSLuca Tettamanti 6252c03d07aSLuca Tettamanti return 1; 6262c03d07aSLuca Tettamanti out: 6272c03d07aSLuca Tettamanti kfree(sensor->acpi_name); 6282c03d07aSLuca Tettamanti kfree(sensor); 6292c03d07aSLuca Tettamanti return err; 6302c03d07aSLuca Tettamanti } 6312c03d07aSLuca Tettamanti 6322c03d07aSLuca Tettamanti static int atk_enumerate_old_hwmon(struct atk_data *data) 6332c03d07aSLuca Tettamanti { 6342c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 6352c03d07aSLuca Tettamanti struct acpi_buffer buf; 6362c03d07aSLuca Tettamanti union acpi_object *pack; 6372c03d07aSLuca Tettamanti acpi_status status; 6382c03d07aSLuca Tettamanti int i, ret; 6392c03d07aSLuca Tettamanti int count = 0; 6402c03d07aSLuca Tettamanti 6412c03d07aSLuca Tettamanti /* Voltages */ 6422c03d07aSLuca Tettamanti buf.length = ACPI_ALLOCATE_BUFFER; 6432c03d07aSLuca Tettamanti status = acpi_evaluate_object_typed(data->atk_handle, 6442c03d07aSLuca Tettamanti METHOD_OLD_ENUM_VLT, NULL, &buf, ACPI_TYPE_PACKAGE); 6452c03d07aSLuca Tettamanti if (status != AE_OK) { 6462c03d07aSLuca Tettamanti dev_warn(dev, METHOD_OLD_ENUM_VLT ": ACPI exception: %s\n", 6472c03d07aSLuca Tettamanti acpi_format_exception(status)); 6482c03d07aSLuca Tettamanti 6492c03d07aSLuca Tettamanti return -ENODEV; 6502c03d07aSLuca Tettamanti } 6512c03d07aSLuca Tettamanti 6522c03d07aSLuca Tettamanti pack = buf.pointer; 6532c03d07aSLuca Tettamanti for (i = 1; i < pack->package.count; i++) { 6542c03d07aSLuca Tettamanti union acpi_object *obj = &pack->package.elements[i]; 6552c03d07aSLuca Tettamanti 6562c03d07aSLuca Tettamanti ret = atk_add_sensor(data, obj); 6572c03d07aSLuca Tettamanti if (ret > 0) 6582c03d07aSLuca Tettamanti count++; 6592c03d07aSLuca Tettamanti } 6602c03d07aSLuca Tettamanti ACPI_FREE(buf.pointer); 6612c03d07aSLuca Tettamanti 6622c03d07aSLuca Tettamanti /* Temperatures */ 6632c03d07aSLuca Tettamanti buf.length = ACPI_ALLOCATE_BUFFER; 6642c03d07aSLuca Tettamanti status = acpi_evaluate_object_typed(data->atk_handle, 6652c03d07aSLuca Tettamanti METHOD_OLD_ENUM_TMP, NULL, &buf, ACPI_TYPE_PACKAGE); 6662c03d07aSLuca Tettamanti if (status != AE_OK) { 6672c03d07aSLuca Tettamanti dev_warn(dev, METHOD_OLD_ENUM_TMP ": ACPI exception: %s\n", 6682c03d07aSLuca Tettamanti acpi_format_exception(status)); 6692c03d07aSLuca Tettamanti 6702c03d07aSLuca Tettamanti ret = -ENODEV; 6712c03d07aSLuca Tettamanti goto cleanup; 6722c03d07aSLuca Tettamanti } 6732c03d07aSLuca Tettamanti 6742c03d07aSLuca Tettamanti pack = buf.pointer; 6752c03d07aSLuca Tettamanti for (i = 1; i < pack->package.count; i++) { 6762c03d07aSLuca Tettamanti union acpi_object *obj = &pack->package.elements[i]; 6772c03d07aSLuca Tettamanti 6782c03d07aSLuca Tettamanti ret = atk_add_sensor(data, obj); 6792c03d07aSLuca Tettamanti if (ret > 0) 6802c03d07aSLuca Tettamanti count++; 6812c03d07aSLuca Tettamanti } 6822c03d07aSLuca Tettamanti ACPI_FREE(buf.pointer); 6832c03d07aSLuca Tettamanti 6842c03d07aSLuca Tettamanti /* Fans */ 6852c03d07aSLuca Tettamanti buf.length = ACPI_ALLOCATE_BUFFER; 6862c03d07aSLuca Tettamanti status = acpi_evaluate_object_typed(data->atk_handle, 6872c03d07aSLuca Tettamanti METHOD_OLD_ENUM_FAN, NULL, &buf, ACPI_TYPE_PACKAGE); 6882c03d07aSLuca Tettamanti if (status != AE_OK) { 6892c03d07aSLuca Tettamanti dev_warn(dev, METHOD_OLD_ENUM_FAN ": ACPI exception: %s\n", 6902c03d07aSLuca Tettamanti acpi_format_exception(status)); 6912c03d07aSLuca Tettamanti 6922c03d07aSLuca Tettamanti ret = -ENODEV; 6932c03d07aSLuca Tettamanti goto cleanup; 6942c03d07aSLuca Tettamanti } 6952c03d07aSLuca Tettamanti 6962c03d07aSLuca Tettamanti pack = buf.pointer; 6972c03d07aSLuca Tettamanti for (i = 1; i < pack->package.count; i++) { 6982c03d07aSLuca Tettamanti union acpi_object *obj = &pack->package.elements[i]; 6992c03d07aSLuca Tettamanti 7002c03d07aSLuca Tettamanti ret = atk_add_sensor(data, obj); 7012c03d07aSLuca Tettamanti if (ret > 0) 7022c03d07aSLuca Tettamanti count++; 7032c03d07aSLuca Tettamanti } 7042c03d07aSLuca Tettamanti ACPI_FREE(buf.pointer); 7052c03d07aSLuca Tettamanti 7062c03d07aSLuca Tettamanti return count; 7072c03d07aSLuca Tettamanti cleanup: 7082c03d07aSLuca Tettamanti atk_free_sensors(data); 7092c03d07aSLuca Tettamanti return ret; 7102c03d07aSLuca Tettamanti } 7112c03d07aSLuca Tettamanti 7122c03d07aSLuca Tettamanti static int atk_enumerate_new_hwmon(struct atk_data *data) 7132c03d07aSLuca Tettamanti { 7142c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 7152c03d07aSLuca Tettamanti struct acpi_buffer buf; 7162c03d07aSLuca Tettamanti acpi_status ret; 7172c03d07aSLuca Tettamanti struct acpi_object_list params; 7182c03d07aSLuca Tettamanti union acpi_object id; 7192c03d07aSLuca Tettamanti union acpi_object *pack; 7202c03d07aSLuca Tettamanti int err; 7212c03d07aSLuca Tettamanti int i; 7222c03d07aSLuca Tettamanti 7232c03d07aSLuca Tettamanti dev_dbg(dev, "Enumerating hwmon sensors\n"); 7242c03d07aSLuca Tettamanti 7252c03d07aSLuca Tettamanti id.type = ACPI_TYPE_INTEGER; 7262c03d07aSLuca Tettamanti id.integer.value = ATK_MUX_HWMON; 7272c03d07aSLuca Tettamanti params.count = 1; 7282c03d07aSLuca Tettamanti params.pointer = &id; 7292c03d07aSLuca Tettamanti 7302c03d07aSLuca Tettamanti buf.length = ACPI_ALLOCATE_BUFFER; 7312c03d07aSLuca Tettamanti ret = acpi_evaluate_object_typed(data->enumerate_handle, NULL, ¶ms, 7322c03d07aSLuca Tettamanti &buf, ACPI_TYPE_PACKAGE); 7332c03d07aSLuca Tettamanti if (ret != AE_OK) { 7342c03d07aSLuca Tettamanti dev_warn(dev, METHOD_ENUMERATE ": ACPI exception: %s\n", 7352c03d07aSLuca Tettamanti acpi_format_exception(ret)); 7362c03d07aSLuca Tettamanti return -ENODEV; 7372c03d07aSLuca Tettamanti } 7382c03d07aSLuca Tettamanti 7392c03d07aSLuca Tettamanti /* Result must be a package */ 7402c03d07aSLuca Tettamanti pack = buf.pointer; 7412c03d07aSLuca Tettamanti 7422c03d07aSLuca Tettamanti if (pack->package.count < 1) { 7432c03d07aSLuca Tettamanti dev_dbg(dev, "%s: hwmon package is too small: %d\n", __func__, 7442c03d07aSLuca Tettamanti pack->package.count); 7452c03d07aSLuca Tettamanti err = -EINVAL; 7462c03d07aSLuca Tettamanti goto out; 7472c03d07aSLuca Tettamanti } 7482c03d07aSLuca Tettamanti 7492c03d07aSLuca Tettamanti for (i = 0; i < pack->package.count; i++) { 7502c03d07aSLuca Tettamanti union acpi_object *obj = &pack->package.elements[i]; 7512c03d07aSLuca Tettamanti 7522c03d07aSLuca Tettamanti atk_add_sensor(data, obj); 7532c03d07aSLuca Tettamanti } 7542c03d07aSLuca Tettamanti 7552c03d07aSLuca Tettamanti err = data->voltage_count + data->temperature_count + data->fan_count; 7562c03d07aSLuca Tettamanti 7572c03d07aSLuca Tettamanti out: 7582c03d07aSLuca Tettamanti ACPI_FREE(buf.pointer); 7592c03d07aSLuca Tettamanti return err; 7602c03d07aSLuca Tettamanti } 7612c03d07aSLuca Tettamanti 7622c03d07aSLuca Tettamanti static int atk_create_files(struct atk_data *data) 7632c03d07aSLuca Tettamanti { 7642c03d07aSLuca Tettamanti struct atk_sensor_data *s; 7652c03d07aSLuca Tettamanti int err; 7662c03d07aSLuca Tettamanti 7672c03d07aSLuca Tettamanti list_for_each_entry(s, &data->sensor_list, list) { 7682c03d07aSLuca Tettamanti err = device_create_file(data->hwmon_dev, &s->input_attr); 7692c03d07aSLuca Tettamanti if (err) 7702c03d07aSLuca Tettamanti return err; 7712c03d07aSLuca Tettamanti err = device_create_file(data->hwmon_dev, &s->label_attr); 7722c03d07aSLuca Tettamanti if (err) 7732c03d07aSLuca Tettamanti return err; 7742c03d07aSLuca Tettamanti err = device_create_file(data->hwmon_dev, &s->limit1_attr); 7752c03d07aSLuca Tettamanti if (err) 7762c03d07aSLuca Tettamanti return err; 7772c03d07aSLuca Tettamanti err = device_create_file(data->hwmon_dev, &s->limit2_attr); 7782c03d07aSLuca Tettamanti if (err) 7792c03d07aSLuca Tettamanti return err; 7802c03d07aSLuca Tettamanti } 7812c03d07aSLuca Tettamanti 7822c03d07aSLuca Tettamanti err = device_create_file(data->hwmon_dev, &atk_name_attr); 7832c03d07aSLuca Tettamanti 7842c03d07aSLuca Tettamanti return err; 7852c03d07aSLuca Tettamanti } 7862c03d07aSLuca Tettamanti 7872c03d07aSLuca Tettamanti static void atk_remove_files(struct atk_data *data) 7882c03d07aSLuca Tettamanti { 7892c03d07aSLuca Tettamanti struct atk_sensor_data *s; 7902c03d07aSLuca Tettamanti 7912c03d07aSLuca Tettamanti list_for_each_entry(s, &data->sensor_list, list) { 7922c03d07aSLuca Tettamanti device_remove_file(data->hwmon_dev, &s->input_attr); 7932c03d07aSLuca Tettamanti device_remove_file(data->hwmon_dev, &s->label_attr); 7942c03d07aSLuca Tettamanti device_remove_file(data->hwmon_dev, &s->limit1_attr); 7952c03d07aSLuca Tettamanti device_remove_file(data->hwmon_dev, &s->limit2_attr); 7962c03d07aSLuca Tettamanti } 7972c03d07aSLuca Tettamanti device_remove_file(data->hwmon_dev, &atk_name_attr); 7982c03d07aSLuca Tettamanti } 7992c03d07aSLuca Tettamanti 8002c03d07aSLuca Tettamanti static void atk_free_sensors(struct atk_data *data) 8012c03d07aSLuca Tettamanti { 8022c03d07aSLuca Tettamanti struct list_head *head = &data->sensor_list; 8032c03d07aSLuca Tettamanti struct atk_sensor_data *s, *tmp; 8042c03d07aSLuca Tettamanti 8052c03d07aSLuca Tettamanti list_for_each_entry_safe(s, tmp, head, list) { 8062c03d07aSLuca Tettamanti kfree(s->acpi_name); 8072c03d07aSLuca Tettamanti kfree(s); 8082c03d07aSLuca Tettamanti } 8092c03d07aSLuca Tettamanti } 8102c03d07aSLuca Tettamanti 8112c03d07aSLuca Tettamanti static int atk_register_hwmon(struct atk_data *data) 8122c03d07aSLuca Tettamanti { 8132c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 8142c03d07aSLuca Tettamanti int err; 8152c03d07aSLuca Tettamanti 8162c03d07aSLuca Tettamanti dev_dbg(dev, "registering hwmon device\n"); 8172c03d07aSLuca Tettamanti data->hwmon_dev = hwmon_device_register(dev); 8182c03d07aSLuca Tettamanti if (IS_ERR(data->hwmon_dev)) 8192c03d07aSLuca Tettamanti return PTR_ERR(data->hwmon_dev); 8202c03d07aSLuca Tettamanti 8212c03d07aSLuca Tettamanti dev_dbg(dev, "populating sysfs directory\n"); 8222c03d07aSLuca Tettamanti err = atk_create_files(data); 8232c03d07aSLuca Tettamanti if (err) 8242c03d07aSLuca Tettamanti goto remove; 8252c03d07aSLuca Tettamanti 8262c03d07aSLuca Tettamanti return 0; 8272c03d07aSLuca Tettamanti remove: 8282c03d07aSLuca Tettamanti /* Cleanup the registered files */ 8292c03d07aSLuca Tettamanti atk_remove_files(data); 8302c03d07aSLuca Tettamanti hwmon_device_unregister(data->hwmon_dev); 8312c03d07aSLuca Tettamanti return err; 8322c03d07aSLuca Tettamanti } 8332c03d07aSLuca Tettamanti 8342c03d07aSLuca Tettamanti static int atk_check_old_if(struct atk_data *data) 8352c03d07aSLuca Tettamanti { 8362c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 8372c03d07aSLuca Tettamanti acpi_handle ret; 8382c03d07aSLuca Tettamanti acpi_status status; 8392c03d07aSLuca Tettamanti 8402c03d07aSLuca Tettamanti /* RTMP: read temperature */ 8412c03d07aSLuca Tettamanti status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_TMP, &ret); 8422c03d07aSLuca Tettamanti if (status != AE_OK) { 8432c03d07aSLuca Tettamanti dev_dbg(dev, "method " METHOD_OLD_READ_TMP " not found: %s\n", 8442c03d07aSLuca Tettamanti acpi_format_exception(status)); 8452c03d07aSLuca Tettamanti return -ENODEV; 8462c03d07aSLuca Tettamanti } 8472c03d07aSLuca Tettamanti data->rtmp_handle = ret; 8482c03d07aSLuca Tettamanti 8492c03d07aSLuca Tettamanti /* RVLT: read voltage */ 8502c03d07aSLuca Tettamanti status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_VLT, &ret); 8512c03d07aSLuca Tettamanti if (status != AE_OK) { 8522c03d07aSLuca Tettamanti dev_dbg(dev, "method " METHOD_OLD_READ_VLT " not found: %s\n", 8532c03d07aSLuca Tettamanti acpi_format_exception(status)); 8542c03d07aSLuca Tettamanti return -ENODEV; 8552c03d07aSLuca Tettamanti } 8562c03d07aSLuca Tettamanti data->rvlt_handle = ret; 8572c03d07aSLuca Tettamanti 8582c03d07aSLuca Tettamanti /* RFAN: read fan status */ 8592c03d07aSLuca Tettamanti status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_FAN, &ret); 8602c03d07aSLuca Tettamanti if (status != AE_OK) { 8612c03d07aSLuca Tettamanti dev_dbg(dev, "method " METHOD_OLD_READ_FAN " not found: %s\n", 8622c03d07aSLuca Tettamanti acpi_format_exception(status)); 8632c03d07aSLuca Tettamanti return -ENODEV; 8642c03d07aSLuca Tettamanti } 8652c03d07aSLuca Tettamanti data->rfan_handle = ret; 8662c03d07aSLuca Tettamanti 8672c03d07aSLuca Tettamanti return 0; 8682c03d07aSLuca Tettamanti } 8692c03d07aSLuca Tettamanti 8702c03d07aSLuca Tettamanti static int atk_check_new_if(struct atk_data *data) 8712c03d07aSLuca Tettamanti { 8722c03d07aSLuca Tettamanti struct device *dev = &data->acpi_dev->dev; 8732c03d07aSLuca Tettamanti acpi_handle ret; 8742c03d07aSLuca Tettamanti acpi_status status; 8752c03d07aSLuca Tettamanti 8762c03d07aSLuca Tettamanti /* Enumeration */ 8772c03d07aSLuca Tettamanti status = acpi_get_handle(data->atk_handle, METHOD_ENUMERATE, &ret); 8782c03d07aSLuca Tettamanti if (status != AE_OK) { 8792c03d07aSLuca Tettamanti dev_dbg(dev, "method " METHOD_ENUMERATE " not found: %s\n", 8802c03d07aSLuca Tettamanti acpi_format_exception(status)); 8812c03d07aSLuca Tettamanti return -ENODEV; 8822c03d07aSLuca Tettamanti } 8832c03d07aSLuca Tettamanti data->enumerate_handle = ret; 8842c03d07aSLuca Tettamanti 8852c03d07aSLuca Tettamanti /* De-multiplexer (read) */ 8862c03d07aSLuca Tettamanti status = acpi_get_handle(data->atk_handle, METHOD_READ, &ret); 8872c03d07aSLuca Tettamanti if (status != AE_OK) { 8882c03d07aSLuca Tettamanti dev_dbg(dev, "method " METHOD_READ " not found: %s\n", 8892c03d07aSLuca Tettamanti acpi_format_exception(status)); 8902c03d07aSLuca Tettamanti return -ENODEV; 8912c03d07aSLuca Tettamanti } 8922c03d07aSLuca Tettamanti data->read_handle = ret; 8932c03d07aSLuca Tettamanti 8942c03d07aSLuca Tettamanti return 0; 8952c03d07aSLuca Tettamanti } 8962c03d07aSLuca Tettamanti 8972c03d07aSLuca Tettamanti static int atk_add(struct acpi_device *device) 8982c03d07aSLuca Tettamanti { 8992c03d07aSLuca Tettamanti acpi_status ret; 9002c03d07aSLuca Tettamanti int err; 9012c03d07aSLuca Tettamanti struct acpi_buffer buf; 9022c03d07aSLuca Tettamanti union acpi_object *obj; 9032c03d07aSLuca Tettamanti struct atk_data *data; 9042c03d07aSLuca Tettamanti 9052c03d07aSLuca Tettamanti dev_dbg(&device->dev, "adding...\n"); 9062c03d07aSLuca Tettamanti 9072c03d07aSLuca Tettamanti data = kzalloc(sizeof(*data), GFP_KERNEL); 9082c03d07aSLuca Tettamanti if (!data) 9092c03d07aSLuca Tettamanti return -ENOMEM; 9102c03d07aSLuca Tettamanti 9112c03d07aSLuca Tettamanti data->acpi_dev = device; 9122c03d07aSLuca Tettamanti data->atk_handle = device->handle; 9132c03d07aSLuca Tettamanti INIT_LIST_HEAD(&data->sensor_list); 9142c03d07aSLuca Tettamanti 9152c03d07aSLuca Tettamanti buf.length = ACPI_ALLOCATE_BUFFER; 9162c03d07aSLuca Tettamanti ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL, 9172c03d07aSLuca Tettamanti &buf, ACPI_TYPE_PACKAGE); 9182c03d07aSLuca Tettamanti if (ret != AE_OK) { 9192c03d07aSLuca Tettamanti dev_dbg(&device->dev, "atk: method MBIF not found\n"); 9202c03d07aSLuca Tettamanti err = -ENODEV; 9212c03d07aSLuca Tettamanti goto out; 9222c03d07aSLuca Tettamanti } 9232c03d07aSLuca Tettamanti 9242c03d07aSLuca Tettamanti obj = buf.pointer; 9252c03d07aSLuca Tettamanti if (obj->package.count >= 2 && 9262c03d07aSLuca Tettamanti obj->package.elements[1].type == ACPI_TYPE_STRING) { 9272c03d07aSLuca Tettamanti dev_dbg(&device->dev, "board ID = %s\n", 9282c03d07aSLuca Tettamanti obj->package.elements[1].string.pointer); 9292c03d07aSLuca Tettamanti } 9302c03d07aSLuca Tettamanti ACPI_FREE(buf.pointer); 9312c03d07aSLuca Tettamanti 9322c03d07aSLuca Tettamanti /* Check for hwmon methods: first check "old" style methods; note that 9332c03d07aSLuca Tettamanti * both may be present: in this case we stick to the old interface; 9342c03d07aSLuca Tettamanti * analysis of multiple DSDTs indicates that when both interfaces 9352c03d07aSLuca Tettamanti * are present the new one (GGRP/GITM) is not functional. 9362c03d07aSLuca Tettamanti */ 9372c03d07aSLuca Tettamanti err = atk_check_old_if(data); 9382c03d07aSLuca Tettamanti if (!err) { 9392c03d07aSLuca Tettamanti dev_dbg(&device->dev, "Using old hwmon interface\n"); 9402c03d07aSLuca Tettamanti data->old_interface = true; 9412c03d07aSLuca Tettamanti } else { 9422c03d07aSLuca Tettamanti err = atk_check_new_if(data); 9432c03d07aSLuca Tettamanti if (err) 9442c03d07aSLuca Tettamanti goto out; 9452c03d07aSLuca Tettamanti 9462c03d07aSLuca Tettamanti dev_dbg(&device->dev, "Using new hwmon interface\n"); 9472c03d07aSLuca Tettamanti data->old_interface = false; 9482c03d07aSLuca Tettamanti } 9492c03d07aSLuca Tettamanti 9502c03d07aSLuca Tettamanti if (data->old_interface) 9512c03d07aSLuca Tettamanti err = atk_enumerate_old_hwmon(data); 9522c03d07aSLuca Tettamanti else 9532c03d07aSLuca Tettamanti err = atk_enumerate_new_hwmon(data); 9542c03d07aSLuca Tettamanti if (err < 0) 9552c03d07aSLuca Tettamanti goto out; 9562c03d07aSLuca Tettamanti if (err == 0) { 9572c03d07aSLuca Tettamanti dev_info(&device->dev, 9582c03d07aSLuca Tettamanti "No usable sensor detected, bailing out\n"); 9592c03d07aSLuca Tettamanti err = -ENODEV; 9602c03d07aSLuca Tettamanti goto out; 9612c03d07aSLuca Tettamanti } 9622c03d07aSLuca Tettamanti 9632c03d07aSLuca Tettamanti err = atk_register_hwmon(data); 9642c03d07aSLuca Tettamanti if (err) 9652c03d07aSLuca Tettamanti goto cleanup; 9662c03d07aSLuca Tettamanti 9672c03d07aSLuca Tettamanti device->driver_data = data; 9682c03d07aSLuca Tettamanti return 0; 9692c03d07aSLuca Tettamanti cleanup: 9702c03d07aSLuca Tettamanti atk_free_sensors(data); 9712c03d07aSLuca Tettamanti out: 9722c03d07aSLuca Tettamanti kfree(data); 9732c03d07aSLuca Tettamanti return err; 9742c03d07aSLuca Tettamanti } 9752c03d07aSLuca Tettamanti 9762c03d07aSLuca Tettamanti static int atk_remove(struct acpi_device *device, int type) 9772c03d07aSLuca Tettamanti { 9782c03d07aSLuca Tettamanti struct atk_data *data = device->driver_data; 9792c03d07aSLuca Tettamanti dev_dbg(&device->dev, "removing...\n"); 9802c03d07aSLuca Tettamanti 9812c03d07aSLuca Tettamanti device->driver_data = NULL; 9822c03d07aSLuca Tettamanti 9832c03d07aSLuca Tettamanti atk_remove_files(data); 9842c03d07aSLuca Tettamanti atk_free_sensors(data); 9852c03d07aSLuca Tettamanti hwmon_device_unregister(data->hwmon_dev); 9862c03d07aSLuca Tettamanti 9872c03d07aSLuca Tettamanti kfree(data); 9882c03d07aSLuca Tettamanti 9892c03d07aSLuca Tettamanti return 0; 9902c03d07aSLuca Tettamanti } 9912c03d07aSLuca Tettamanti 9922c03d07aSLuca Tettamanti static int __init atk0110_init(void) 9932c03d07aSLuca Tettamanti { 9942c03d07aSLuca Tettamanti int ret; 9952c03d07aSLuca Tettamanti 9962c03d07aSLuca Tettamanti ret = acpi_bus_register_driver(&atk_driver); 9972c03d07aSLuca Tettamanti if (ret) 9982c03d07aSLuca Tettamanti pr_info("atk: acpi_bus_register_driver failed: %d\n", ret); 9992c03d07aSLuca Tettamanti 10002c03d07aSLuca Tettamanti return ret; 10012c03d07aSLuca Tettamanti } 10022c03d07aSLuca Tettamanti 10032c03d07aSLuca Tettamanti static void __exit atk0110_exit(void) 10042c03d07aSLuca Tettamanti { 10052c03d07aSLuca Tettamanti acpi_bus_unregister_driver(&atk_driver); 10062c03d07aSLuca Tettamanti } 10072c03d07aSLuca Tettamanti 10082c03d07aSLuca Tettamanti module_init(atk0110_init); 10092c03d07aSLuca Tettamanti module_exit(atk0110_exit); 10102c03d07aSLuca Tettamanti 10112c03d07aSLuca Tettamanti MODULE_LICENSE("GPL"); 1012