1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * fan_attr.c - Create extra attributes for ACPI Fan driver |
4 | * |
5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
7 | * Copyright (C) 2022 Intel Corporation. All rights reserved. |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/init.h> |
13 | #include <linux/acpi.h> |
14 | |
15 | #include "fan.h" |
16 | |
17 | MODULE_LICENSE("GPL" ); |
18 | |
19 | static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf) |
20 | { |
21 | struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr); |
22 | int count; |
23 | |
24 | if (fps->control == 0xFFFFFFFF || fps->control > 100) |
25 | count = scnprintf(buf, PAGE_SIZE, fmt: "not-defined:" ); |
26 | else |
27 | count = scnprintf(buf, PAGE_SIZE, fmt: "%lld:" , fps->control); |
28 | |
29 | if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) |
30 | count += sysfs_emit_at(buf, at: count, fmt: "not-defined:" ); |
31 | else |
32 | count += sysfs_emit_at(buf, at: count, fmt: "%lld:" , fps->trip_point); |
33 | |
34 | if (fps->speed == 0xFFFFFFFF) |
35 | count += sysfs_emit_at(buf, at: count, fmt: "not-defined:" ); |
36 | else |
37 | count += sysfs_emit_at(buf, at: count, fmt: "%lld:" , fps->speed); |
38 | |
39 | if (fps->noise_level == 0xFFFFFFFF) |
40 | count += sysfs_emit_at(buf, at: count, fmt: "not-defined:" ); |
41 | else |
42 | count += sysfs_emit_at(buf, at: count, fmt: "%lld:" , fps->noise_level * 100); |
43 | |
44 | if (fps->power == 0xFFFFFFFF) |
45 | count += sysfs_emit_at(buf, at: count, fmt: "not-defined\n" ); |
46 | else |
47 | count += sysfs_emit_at(buf, at: count, fmt: "%lld\n" , fps->power); |
48 | |
49 | return count; |
50 | } |
51 | |
52 | static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) |
53 | { |
54 | struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); |
55 | struct acpi_fan_fst fst; |
56 | int status; |
57 | |
58 | status = acpi_fan_get_fst(device: acpi_dev, fst: &fst); |
59 | if (status) |
60 | return status; |
61 | |
62 | return sprintf(buf, fmt: "%lld\n" , fst.speed); |
63 | } |
64 | |
65 | static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf) |
66 | { |
67 | struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); |
68 | struct acpi_fan *fan = acpi_driver_data(d: acpi_dev); |
69 | |
70 | return sprintf(buf, fmt: "%d\n" , fan->fif.fine_grain_ctrl); |
71 | } |
72 | |
73 | int acpi_fan_create_attributes(struct acpi_device *device) |
74 | { |
75 | struct acpi_fan *fan = acpi_driver_data(d: device); |
76 | int i, status; |
77 | |
78 | sysfs_attr_init(&fan->fine_grain_control.attr); |
79 | fan->fine_grain_control.show = show_fine_grain_control; |
80 | fan->fine_grain_control.store = NULL; |
81 | fan->fine_grain_control.attr.name = "fine_grain_control" ; |
82 | fan->fine_grain_control.attr.mode = 0444; |
83 | status = sysfs_create_file(kobj: &device->dev.kobj, attr: &fan->fine_grain_control.attr); |
84 | if (status) |
85 | return status; |
86 | |
87 | /* _FST is present if we are here */ |
88 | sysfs_attr_init(&fan->fst_speed.attr); |
89 | fan->fst_speed.show = show_fan_speed; |
90 | fan->fst_speed.store = NULL; |
91 | fan->fst_speed.attr.name = "fan_speed_rpm" ; |
92 | fan->fst_speed.attr.mode = 0444; |
93 | status = sysfs_create_file(kobj: &device->dev.kobj, attr: &fan->fst_speed.attr); |
94 | if (status) |
95 | goto rem_fine_grain_attr; |
96 | |
97 | for (i = 0; i < fan->fps_count; ++i) { |
98 | struct acpi_fan_fps *fps = &fan->fps[i]; |
99 | |
100 | snprintf(buf: fps->name, ACPI_FPS_NAME_LEN, fmt: "state%d" , i); |
101 | sysfs_attr_init(&fps->dev_attr.attr); |
102 | fps->dev_attr.show = show_state; |
103 | fps->dev_attr.store = NULL; |
104 | fps->dev_attr.attr.name = fps->name; |
105 | fps->dev_attr.attr.mode = 0444; |
106 | status = sysfs_create_file(kobj: &device->dev.kobj, attr: &fps->dev_attr.attr); |
107 | if (status) { |
108 | int j; |
109 | |
110 | for (j = 0; j < i; ++j) |
111 | sysfs_remove_file(kobj: &device->dev.kobj, attr: &fan->fps[j].dev_attr.attr); |
112 | goto rem_fst_attr; |
113 | } |
114 | } |
115 | |
116 | return 0; |
117 | |
118 | rem_fst_attr: |
119 | sysfs_remove_file(kobj: &device->dev.kobj, attr: &fan->fst_speed.attr); |
120 | |
121 | rem_fine_grain_attr: |
122 | sysfs_remove_file(kobj: &device->dev.kobj, attr: &fan->fine_grain_control.attr); |
123 | |
124 | return status; |
125 | } |
126 | |
127 | void acpi_fan_delete_attributes(struct acpi_device *device) |
128 | { |
129 | struct acpi_fan *fan = acpi_driver_data(d: device); |
130 | int i; |
131 | |
132 | for (i = 0; i < fan->fps_count; ++i) |
133 | sysfs_remove_file(kobj: &device->dev.kobj, attr: &fan->fps[i].dev_attr.attr); |
134 | |
135 | sysfs_remove_file(kobj: &device->dev.kobj, attr: &fan->fst_speed.attr); |
136 | sysfs_remove_file(kobj: &device->dev.kobj, attr: &fan->fine_grain_control.attr); |
137 | } |
138 | |