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, "not-defined:"); 26 else 27 count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); 28 29 if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) 30 count += sysfs_emit_at(buf, count, "not-defined:"); 31 else 32 count += sysfs_emit_at(buf, count, "%lld:", fps->trip_point); 33 34 if (fps->speed == 0xFFFFFFFF) 35 count += sysfs_emit_at(buf, count, "not-defined:"); 36 else 37 count += sysfs_emit_at(buf, count, "%lld:", fps->speed); 38 39 if (fps->noise_level == 0xFFFFFFFF) 40 count += sysfs_emit_at(buf, count, "not-defined:"); 41 else 42 count += sysfs_emit_at(buf, count, "%lld:", fps->noise_level * 100); 43 44 if (fps->power == 0xFFFFFFFF) 45 count += sysfs_emit_at(buf, count, "not-defined\n"); 46 else 47 count += sysfs_emit_at(buf, count, "%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(acpi_dev, &fst); 59 if (status) 60 return status; 61 62 return sprintf(buf, "%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(acpi_dev); 69 70 return sprintf(buf, "%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(device); 76 int i, status; 77 78 /* _FST is present if we are here */ 79 sysfs_attr_init(&fan->fst_speed.attr); 80 fan->fst_speed.show = show_fan_speed; 81 fan->fst_speed.store = NULL; 82 fan->fst_speed.attr.name = "fan_speed_rpm"; 83 fan->fst_speed.attr.mode = 0444; 84 status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr); 85 if (status) 86 return status; 87 88 if (!fan->acpi4) 89 return 0; 90 91 sysfs_attr_init(&fan->fine_grain_control.attr); 92 fan->fine_grain_control.show = show_fine_grain_control; 93 fan->fine_grain_control.store = NULL; 94 fan->fine_grain_control.attr.name = "fine_grain_control"; 95 fan->fine_grain_control.attr.mode = 0444; 96 status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr); 97 if (status) 98 goto rem_fst_attr; 99 100 for (i = 0; i < fan->fps_count; ++i) { 101 struct acpi_fan_fps *fps = &fan->fps[i]; 102 103 snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i); 104 sysfs_attr_init(&fps->dev_attr.attr); 105 fps->dev_attr.show = show_state; 106 fps->dev_attr.store = NULL; 107 fps->dev_attr.attr.name = fps->name; 108 fps->dev_attr.attr.mode = 0444; 109 status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr); 110 if (status) { 111 int j; 112 113 for (j = 0; j < i; ++j) 114 sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); 115 goto rem_fine_grain_attr; 116 } 117 } 118 119 return 0; 120 121 rem_fine_grain_attr: 122 sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); 123 124 rem_fst_attr: 125 sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); 126 127 return status; 128 } 129 130 void acpi_fan_delete_attributes(struct acpi_device *device) 131 { 132 struct acpi_fan *fan = acpi_driver_data(device); 133 int i; 134 135 sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); 136 137 if (!fan->acpi4) 138 return; 139 140 for (i = 0; i < fan->fps_count; ++i) 141 sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); 142 143 sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); 144 } 145