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 += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); 31 else 32 count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point); 33 34 if (fps->speed == 0xFFFFFFFF) 35 count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); 36 else 37 count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed); 38 39 if (fps->noise_level == 0xFFFFFFFF) 40 count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); 41 else 42 count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100); 43 44 if (fps->power == 0xFFFFFFFF) 45 count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n"); 46 else 47 count += scnprintf(&buf[count], PAGE_SIZE - 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 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(&device->dev.kobj, &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(&device->dev.kobj, &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(fps->name, ACPI_FPS_NAME_LEN, "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(&device->dev.kobj, &fps->dev_attr.attr); 107 if (status) { 108 int j; 109 110 for (j = 0; j < i; ++j) 111 sysfs_remove_file(&device->dev.kobj, &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(&device->dev.kobj, &fan->fst_speed.attr); 120 121 rem_fine_grain_attr: 122 sysfs_remove_file(&device->dev.kobj, &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(device); 130 int i; 131 132 for (i = 0; i < fan->fps_count; ++i) 133 sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); 134 135 sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); 136 sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); 137 } 138