diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/asus_atk0110.c | 185 |
1 files changed, 184 insertions, 1 deletions
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 19f3fd32e10a..5a3ee00c0e7d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c | |||
@@ -35,18 +35,22 @@ | |||
35 | #define METHOD_OLD_ENUM_FAN "FSIF" | 35 | #define METHOD_OLD_ENUM_FAN "FSIF" |
36 | 36 | ||
37 | #define ATK_MUX_HWMON 0x00000006ULL | 37 | #define ATK_MUX_HWMON 0x00000006ULL |
38 | #define ATK_MUX_MGMT 0x00000011ULL | ||
38 | 39 | ||
39 | #define ATK_CLASS_MASK 0xff000000ULL | 40 | #define ATK_CLASS_MASK 0xff000000ULL |
40 | #define ATK_CLASS_FREQ_CTL 0x03000000ULL | 41 | #define ATK_CLASS_FREQ_CTL 0x03000000ULL |
41 | #define ATK_CLASS_FAN_CTL 0x04000000ULL | 42 | #define ATK_CLASS_FAN_CTL 0x04000000ULL |
42 | #define ATK_CLASS_HWMON 0x06000000ULL | 43 | #define ATK_CLASS_HWMON 0x06000000ULL |
44 | #define ATK_CLASS_MGMT 0x11000000ULL | ||
43 | 45 | ||
44 | #define ATK_TYPE_MASK 0x00ff0000ULL | 46 | #define ATK_TYPE_MASK 0x00ff0000ULL |
45 | #define HWMON_TYPE_VOLT 0x00020000ULL | 47 | #define HWMON_TYPE_VOLT 0x00020000ULL |
46 | #define HWMON_TYPE_TEMP 0x00030000ULL | 48 | #define HWMON_TYPE_TEMP 0x00030000ULL |
47 | #define HWMON_TYPE_FAN 0x00040000ULL | 49 | #define HWMON_TYPE_FAN 0x00040000ULL |
48 | 50 | ||
49 | #define HWMON_SENSOR_ID_MASK 0x0000ffffULL | 51 | #define ATK_ELEMENT_ID_MASK 0x0000ffffULL |
52 | |||
53 | #define ATK_EC_ID 0x11060004ULL | ||
50 | 54 | ||
51 | enum atk_pack_member { | 55 | enum atk_pack_member { |
52 | HWMON_PACK_FLAGS, | 56 | HWMON_PACK_FLAGS, |
@@ -89,6 +93,9 @@ struct atk_data { | |||
89 | /* new inteface */ | 93 | /* new inteface */ |
90 | acpi_handle enumerate_handle; | 94 | acpi_handle enumerate_handle; |
91 | acpi_handle read_handle; | 95 | acpi_handle read_handle; |
96 | acpi_handle write_handle; | ||
97 | |||
98 | bool disable_ec; | ||
92 | 99 | ||
93 | int voltage_count; | 100 | int voltage_count; |
94 | int temperature_count; | 101 | int temperature_count; |
@@ -529,6 +536,43 @@ static union acpi_object *atk_gitm(struct atk_data *data, u64 id) | |||
529 | return obj; | 536 | return obj; |
530 | } | 537 | } |
531 | 538 | ||
539 | static union acpi_object *atk_sitm(struct atk_data *data, | ||
540 | struct atk_acpi_input_buf *buf) | ||
541 | { | ||
542 | struct device *dev = &data->acpi_dev->dev; | ||
543 | struct acpi_object_list params; | ||
544 | union acpi_object tmp; | ||
545 | struct acpi_buffer ret; | ||
546 | union acpi_object *obj; | ||
547 | acpi_status status; | ||
548 | |||
549 | tmp.type = ACPI_TYPE_BUFFER; | ||
550 | tmp.buffer.pointer = (u8 *)buf; | ||
551 | tmp.buffer.length = sizeof(*buf); | ||
552 | |||
553 | params.count = 1; | ||
554 | params.pointer = &tmp; | ||
555 | |||
556 | ret.length = ACPI_ALLOCATE_BUFFER; | ||
557 | status = acpi_evaluate_object_typed(data->write_handle, NULL, ¶ms, | ||
558 | &ret, ACPI_TYPE_BUFFER); | ||
559 | if (status != AE_OK) { | ||
560 | dev_warn(dev, "SITM[%#x] ACPI exception: %s\n", buf->id, | ||
561 | acpi_format_exception(status)); | ||
562 | return ERR_PTR(-EIO); | ||
563 | } | ||
564 | obj = ret.pointer; | ||
565 | |||
566 | /* Sanity check */ | ||
567 | if (obj->buffer.length < 8) { | ||
568 | dev_warn(dev, "Unexpected ASBF length: %u\n", | ||
569 | obj->buffer.length); | ||
570 | ACPI_FREE(obj); | ||
571 | return ERR_PTR(-EIO); | ||
572 | } | ||
573 | return obj; | ||
574 | } | ||
575 | |||
532 | static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) | 576 | static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) |
533 | { | 577 | { |
534 | struct atk_data *data = sensor->data; | 578 | struct atk_data *data = sensor->data; |
@@ -784,6 +828,111 @@ cleanup: | |||
784 | return ret; | 828 | return ret; |
785 | } | 829 | } |
786 | 830 | ||
831 | static int atk_ec_present(struct atk_data *data) | ||
832 | { | ||
833 | struct device *dev = &data->acpi_dev->dev; | ||
834 | union acpi_object *pack; | ||
835 | union acpi_object *ec; | ||
836 | int ret; | ||
837 | int i; | ||
838 | |||
839 | pack = atk_ggrp(data, ATK_MUX_MGMT); | ||
840 | if (IS_ERR(pack)) { | ||
841 | if (PTR_ERR(pack) == -ENOENT) { | ||
842 | /* The MGMT class does not exists - that's ok */ | ||
843 | dev_dbg(dev, "Class %#llx not found\n", ATK_MUX_MGMT); | ||
844 | return 0; | ||
845 | } | ||
846 | return PTR_ERR(pack); | ||
847 | } | ||
848 | |||
849 | /* Search the EC */ | ||
850 | ec = NULL; | ||
851 | for (i = 0; i < pack->package.count; i++) { | ||
852 | union acpi_object *obj = &pack->package.elements[i]; | ||
853 | union acpi_object *id; | ||
854 | |||
855 | if (obj->type != ACPI_TYPE_PACKAGE) | ||
856 | continue; | ||
857 | |||
858 | id = &obj->package.elements[0]; | ||
859 | if (id->type != ACPI_TYPE_INTEGER) | ||
860 | continue; | ||
861 | |||
862 | if (id->integer.value == ATK_EC_ID) { | ||
863 | ec = obj; | ||
864 | break; | ||
865 | } | ||
866 | } | ||
867 | |||
868 | ret = (ec != NULL); | ||
869 | if (!ret) | ||
870 | /* The system has no EC */ | ||
871 | dev_dbg(dev, "EC not found\n"); | ||
872 | |||
873 | ACPI_FREE(pack); | ||
874 | return ret; | ||
875 | } | ||
876 | |||
877 | static int atk_ec_enabled(struct atk_data *data) | ||
878 | { | ||
879 | struct device *dev = &data->acpi_dev->dev; | ||
880 | union acpi_object *obj; | ||
881 | struct atk_acpi_ret_buffer *buf; | ||
882 | int err; | ||
883 | |||
884 | obj = atk_gitm(data, ATK_EC_ID); | ||
885 | if (IS_ERR(obj)) { | ||
886 | dev_err(dev, "Unable to query EC status\n"); | ||
887 | return PTR_ERR(obj); | ||
888 | } | ||
889 | buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; | ||
890 | |||
891 | if (buf->flags == 0) { | ||
892 | dev_err(dev, "Unable to query EC status\n"); | ||
893 | err = -EIO; | ||
894 | } else { | ||
895 | err = (buf->value != 0); | ||
896 | dev_dbg(dev, "EC is %sabled\n", | ||
897 | err ? "en" : "dis"); | ||
898 | } | ||
899 | |||
900 | ACPI_FREE(obj); | ||
901 | return err; | ||
902 | } | ||
903 | |||
904 | static int atk_ec_ctl(struct atk_data *data, int enable) | ||
905 | { | ||
906 | struct device *dev = &data->acpi_dev->dev; | ||
907 | union acpi_object *obj; | ||
908 | struct atk_acpi_input_buf sitm; | ||
909 | struct atk_acpi_ret_buffer *ec_ret; | ||
910 | int err = 0; | ||
911 | |||
912 | sitm.id = ATK_EC_ID; | ||
913 | sitm.param1 = enable; | ||
914 | sitm.param2 = 0; | ||
915 | |||
916 | obj = atk_sitm(data, &sitm); | ||
917 | if (IS_ERR(obj)) { | ||
918 | dev_err(dev, "Failed to %sable the EC\n", | ||
919 | enable ? "en" : "dis"); | ||
920 | return PTR_ERR(obj); | ||
921 | } | ||
922 | ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; | ||
923 | if (ec_ret->flags == 0) { | ||
924 | dev_err(dev, "Failed to %sable the EC\n", | ||
925 | enable ? "en" : "dis"); | ||
926 | err = -EIO; | ||
927 | } else { | ||
928 | dev_info(dev, "EC %sabled\n", | ||
929 | enable ? "en" : "dis"); | ||
930 | } | ||
931 | |||
932 | ACPI_FREE(obj); | ||
933 | return err; | ||
934 | } | ||
935 | |||
787 | static int atk_enumerate_new_hwmon(struct atk_data *data) | 936 | static int atk_enumerate_new_hwmon(struct atk_data *data) |
788 | { | 937 | { |
789 | struct device *dev = &data->acpi_dev->dev; | 938 | struct device *dev = &data->acpi_dev->dev; |
@@ -791,6 +940,23 @@ static int atk_enumerate_new_hwmon(struct atk_data *data) | |||
791 | int err; | 940 | int err; |
792 | int i; | 941 | int i; |
793 | 942 | ||
943 | err = atk_ec_present(data); | ||
944 | if (err < 0) | ||
945 | return err; | ||
946 | if (err) { | ||
947 | err = atk_ec_enabled(data); | ||
948 | if (err < 0) | ||
949 | return err; | ||
950 | /* If the EC was disabled we will disable it again on unload */ | ||
951 | data->disable_ec = err; | ||
952 | |||
953 | err = atk_ec_ctl(data, 1); | ||
954 | if (err) { | ||
955 | data->disable_ec = false; | ||
956 | return err; | ||
957 | } | ||
958 | } | ||
959 | |||
794 | dev_dbg(dev, "Enumerating hwmon sensors\n"); | 960 | dev_dbg(dev, "Enumerating hwmon sensors\n"); |
795 | 961 | ||
796 | pack = atk_ggrp(data, ATK_MUX_HWMON); | 962 | pack = atk_ggrp(data, ATK_MUX_HWMON); |
@@ -941,6 +1107,15 @@ static int atk_check_new_if(struct atk_data *data) | |||
941 | } | 1107 | } |
942 | data->read_handle = ret; | 1108 | data->read_handle = ret; |
943 | 1109 | ||
1110 | /* De-multiplexer (write) */ | ||
1111 | status = acpi_get_handle(data->atk_handle, METHOD_WRITE, &ret); | ||
1112 | if (status != AE_OK) { | ||
1113 | dev_dbg(dev, "method " METHOD_READ " not found: %s\n", | ||
1114 | acpi_format_exception(status)); | ||
1115 | return -ENODEV; | ||
1116 | } | ||
1117 | data->write_handle = ret; | ||
1118 | |||
944 | return 0; | 1119 | return 0; |
945 | } | 1120 | } |
946 | 1121 | ||
@@ -961,6 +1136,7 @@ static int atk_add(struct acpi_device *device) | |||
961 | data->acpi_dev = device; | 1136 | data->acpi_dev = device; |
962 | data->atk_handle = device->handle; | 1137 | data->atk_handle = device->handle; |
963 | INIT_LIST_HEAD(&data->sensor_list); | 1138 | INIT_LIST_HEAD(&data->sensor_list); |
1139 | data->disable_ec = false; | ||
964 | 1140 | ||
965 | buf.length = ACPI_ALLOCATE_BUFFER; | 1141 | buf.length = ACPI_ALLOCATE_BUFFER; |
966 | ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL, | 1142 | ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL, |
@@ -1019,6 +1195,8 @@ static int atk_add(struct acpi_device *device) | |||
1019 | cleanup: | 1195 | cleanup: |
1020 | atk_free_sensors(data); | 1196 | atk_free_sensors(data); |
1021 | out: | 1197 | out: |
1198 | if (data->disable_ec) | ||
1199 | atk_ec_ctl(data, 0); | ||
1022 | kfree(data); | 1200 | kfree(data); |
1023 | return err; | 1201 | return err; |
1024 | } | 1202 | } |
@@ -1034,6 +1212,11 @@ static int atk_remove(struct acpi_device *device, int type) | |||
1034 | atk_free_sensors(data); | 1212 | atk_free_sensors(data); |
1035 | hwmon_device_unregister(data->hwmon_dev); | 1213 | hwmon_device_unregister(data->hwmon_dev); |
1036 | 1214 | ||
1215 | if (data->disable_ec) { | ||
1216 | if (atk_ec_ctl(data, 0)) | ||
1217 | dev_err(&device->dev, "Failed to disable EC\n"); | ||
1218 | } | ||
1219 | |||
1037 | kfree(data); | 1220 | kfree(data); |
1038 | 1221 | ||
1039 | return 0; | 1222 | return 0; |