summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/fan_attr.c
blob: f4f6e2381f1d3af4e3071d325da81dbf9d87ee01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  fan_attr.c - Create extra attributes for ACPI Fan driver
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *  Copyright (C) 2022 Intel Corporation. All rights reserved.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>

#include "fan.h"

MODULE_LICENSE("GPL");

static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr);
	int count;

	if (fps->control == 0xFFFFFFFF || fps->control > 100)
		count = scnprintf(buf, PAGE_SIZE, "not-defined:");
	else
		count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control);

	if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
		count += sysfs_emit_at(buf, count, "not-defined:");
	else
		count += sysfs_emit_at(buf, count, "%lld:", fps->trip_point);

	if (fps->speed == 0xFFFFFFFF)
		count += sysfs_emit_at(buf, count, "not-defined:");
	else
		count += sysfs_emit_at(buf, count, "%lld:", fps->speed);

	if (fps->noise_level == 0xFFFFFFFF)
		count += sysfs_emit_at(buf, count, "not-defined:");
	else
		count += sysfs_emit_at(buf, count, "%lld:", fps->noise_level * 100);

	if (fps->power == 0xFFFFFFFF)
		count += sysfs_emit_at(buf, count, "not-defined\n");
	else
		count += sysfs_emit_at(buf, count, "%lld\n", fps->power);

	return count;
}

static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
	struct acpi_fan_fst fst;
	int status;

	status = acpi_fan_get_fst(acpi_dev, &fst);
	if (status)
		return status;

	return sprintf(buf, "%lld\n", fst.speed);
}

static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
	struct acpi_fan *fan = acpi_driver_data(acpi_dev);

	return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl);
}

int acpi_fan_create_attributes(struct acpi_device *device)
{
	struct acpi_fan *fan = acpi_driver_data(device);
	int i, status;

	sysfs_attr_init(&fan->fine_grain_control.attr);
	fan->fine_grain_control.show = show_fine_grain_control;
	fan->fine_grain_control.store = NULL;
	fan->fine_grain_control.attr.name = "fine_grain_control";
	fan->fine_grain_control.attr.mode = 0444;
	status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr);
	if (status)
		return status;

	/* _FST is present if we are here */
	sysfs_attr_init(&fan->fst_speed.attr);
	fan->fst_speed.show = show_fan_speed;
	fan->fst_speed.store = NULL;
	fan->fst_speed.attr.name = "fan_speed_rpm";
	fan->fst_speed.attr.mode = 0444;
	status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr);
	if (status)
		goto rem_fine_grain_attr;

	for (i = 0; i < fan->fps_count; ++i) {
		struct acpi_fan_fps *fps = &fan->fps[i];

		snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i);
		sysfs_attr_init(&fps->dev_attr.attr);
		fps->dev_attr.show = show_state;
		fps->dev_attr.store = NULL;
		fps->dev_attr.attr.name = fps->name;
		fps->dev_attr.attr.mode = 0444;
		status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr);
		if (status) {
			int j;

			for (j = 0; j < i; ++j)
				sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
			goto rem_fst_attr;
		}
	}

	return 0;

rem_fst_attr:
	sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);

rem_fine_grain_attr:
	sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);

	return status;
}

void acpi_fan_delete_attributes(struct acpi_device *device)
{
	struct acpi_fan *fan = acpi_driver_data(device);
	int i;

	for (i = 0; i < fan->fps_count; ++i)
		sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);

	sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
	sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
}