xref: /linux/drivers/platform/x86/dasharo-acpi.c (revision 1193e205dbb6feca917dc8e1862ffcdf2194234b)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Dasharo ACPI Driver
4  */
5 
6 #include <linux/acpi.h>
7 #include <linux/array_size.h>
8 #include <linux/hwmon.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/types.h>
12 #include <linux/units.h>
13 
14 enum dasharo_feature {
15 	DASHARO_FEATURE_TEMPERATURE = 0,
16 	DASHARO_FEATURE_FAN_PWM,
17 	DASHARO_FEATURE_FAN_TACH,
18 	DASHARO_FEATURE_MAX
19 };
20 
21 enum dasharo_temperature {
22 	DASHARO_TEMPERATURE_CPU_PACKAGE = 0,
23 	DASHARO_TEMPERATURE_CPU_CORE,
24 	DASHARO_TEMPERATURE_GPU,
25 	DASHARO_TEMPERATURE_BOARD,
26 	DASHARO_TEMPERATURE_CHASSIS,
27 	DASHARO_TEMPERATURE_MAX
28 };
29 
30 enum dasharo_fan {
31 	DASHARO_FAN_CPU = 0,
32 	DASHARO_FAN_GPU,
33 	DASHARO_FAN_CHASSIS,
34 	DASHARO_FAN_MAX
35 };
36 
37 #define MAX_GROUPS_PER_FEAT 8
38 
39 static const char * const dasharo_group_names[DASHARO_FEATURE_MAX][MAX_GROUPS_PER_FEAT] = {
40 	[DASHARO_FEATURE_TEMPERATURE] = {
41 		[DASHARO_TEMPERATURE_CPU_PACKAGE] = "CPU Package",
42 		[DASHARO_TEMPERATURE_CPU_CORE] = "CPU Core",
43 		[DASHARO_TEMPERATURE_GPU] = "GPU",
44 		[DASHARO_TEMPERATURE_BOARD] = "Board",
45 		[DASHARO_TEMPERATURE_CHASSIS] = "Chassis",
46 	},
47 	[DASHARO_FEATURE_FAN_PWM] = {
48 		[DASHARO_FAN_CPU] = "CPU",
49 		[DASHARO_FAN_GPU] = "GPU",
50 		[DASHARO_FAN_CHASSIS] = "Chassis",
51 	},
52 	[DASHARO_FEATURE_FAN_TACH] = {
53 		[DASHARO_FAN_CPU] = "CPU",
54 		[DASHARO_FAN_GPU] = "GPU",
55 		[DASHARO_FAN_CHASSIS] = "Chassis",
56 	},
57 };
58 
59 struct dasharo_capability {
60 	unsigned int group;
61 	unsigned int index;
62 	char name[16];
63 };
64 
65 #define MAX_CAPS_PER_FEAT 24
66 
67 struct dasharo_data {
68 	struct platform_device *pdev;
69 	int caps_found[DASHARO_FEATURE_MAX];
70 	struct dasharo_capability capabilities[DASHARO_FEATURE_MAX][MAX_CAPS_PER_FEAT];
71 };
72 
dasharo_get_feature_cap_count(struct dasharo_data * data,enum dasharo_feature feat,int cap)73 static int dasharo_get_feature_cap_count(struct dasharo_data *data, enum dasharo_feature feat, int cap)
74 {
75 	struct acpi_object_list obj_list;
76 	union acpi_object obj[2];
77 	acpi_handle handle;
78 	acpi_status status;
79 	u64 count;
80 
81 	obj[0].type = ACPI_TYPE_INTEGER;
82 	obj[0].integer.value = feat;
83 	obj[1].type = ACPI_TYPE_INTEGER;
84 	obj[1].integer.value = cap;
85 	obj_list.count = 2;
86 	obj_list.pointer = &obj[0];
87 
88 	handle = ACPI_HANDLE(&data->pdev->dev);
89 	status = acpi_evaluate_integer(handle, "GFCP", &obj_list, &count);
90 	if (ACPI_FAILURE(status))
91 		return -ENODEV;
92 
93 	return count;
94 }
95 
dasharo_read_channel(struct dasharo_data * data,char * method,enum dasharo_feature feat,int channel,long * value)96 static int dasharo_read_channel(struct dasharo_data *data, char *method, enum dasharo_feature feat, int channel, long *value)
97 {
98 	struct acpi_object_list obj_list;
99 	union acpi_object obj[2];
100 	acpi_handle handle;
101 	acpi_status status;
102 	u64 val;
103 
104 	if (feat >= ARRAY_SIZE(data->capabilities))
105 		return -EINVAL;
106 
107 	if (channel >= data->caps_found[feat])
108 		return -EINVAL;
109 
110 	obj[0].type = ACPI_TYPE_INTEGER;
111 	obj[0].integer.value = data->capabilities[feat][channel].group;
112 	obj[1].type = ACPI_TYPE_INTEGER;
113 	obj[1].integer.value = data->capabilities[feat][channel].index;
114 	obj_list.count = 2;
115 	obj_list.pointer = &obj[0];
116 
117 	handle = ACPI_HANDLE(&data->pdev->dev);
118 	status = acpi_evaluate_integer(handle, method, &obj_list, &val);
119 	if (ACPI_FAILURE(status))
120 		return -ENODEV;
121 
122 	*value = val;
123 	return 0;
124 }
125 
dasharo_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)126 static int dasharo_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
127 			      u32 attr, int channel, long *val)
128 {
129 	struct dasharo_data *data = dev_get_drvdata(dev);
130 	long value;
131 	int ret;
132 
133 	switch (type) {
134 	case hwmon_temp:
135 		ret = dasharo_read_channel(data, "GTMP", DASHARO_FEATURE_TEMPERATURE, channel, &value);
136 		if (!ret)
137 			*val = value * MILLIDEGREE_PER_DEGREE;
138 		break;
139 	case hwmon_fan:
140 		ret = dasharo_read_channel(data, "GFTH", DASHARO_FEATURE_FAN_TACH, channel, &value);
141 		if (!ret)
142 			*val = value;
143 		break;
144 	case hwmon_pwm:
145 		ret = dasharo_read_channel(data, "GFDC", DASHARO_FEATURE_FAN_PWM, channel, &value);
146 		if (!ret)
147 			*val = value;
148 		break;
149 	default:
150 		return -ENODEV;
151 		break;
152 	}
153 
154 	return ret;
155 }
156 
dasharo_hwmon_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)157 static int dasharo_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
158 				     u32 attr, int channel, const char **str)
159 {
160 	struct dasharo_data *data = dev_get_drvdata(dev);
161 
162 	switch (type) {
163 	case hwmon_temp:
164 		if (channel >= data->caps_found[DASHARO_FEATURE_TEMPERATURE])
165 			return -EINVAL;
166 
167 		*str = data->capabilities[DASHARO_FEATURE_TEMPERATURE][channel].name;
168 		break;
169 	case hwmon_fan:
170 		if (channel >= data->caps_found[DASHARO_FEATURE_FAN_TACH])
171 			return -EINVAL;
172 
173 		*str = data->capabilities[DASHARO_FEATURE_FAN_TACH][channel].name;
174 		break;
175 	default:
176 		return -EOPNOTSUPP;
177 	}
178 
179 	return 0;
180 }
181 
dasharo_hwmon_is_visible(const void * drvdata,enum hwmon_sensor_types type,u32 attr,int channel)182 static umode_t dasharo_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
183 					u32 attr, int channel)
184 {
185 	const struct dasharo_data *data = drvdata;
186 
187 	switch (type) {
188 	case hwmon_temp:
189 		if (channel < data->caps_found[DASHARO_FEATURE_TEMPERATURE])
190 			return 0444;
191 		break;
192 	case hwmon_pwm:
193 		if (channel < data->caps_found[DASHARO_FEATURE_FAN_PWM])
194 			return 0444;
195 		break;
196 	case hwmon_fan:
197 		if (channel < data->caps_found[DASHARO_FEATURE_FAN_TACH])
198 			return 0444;
199 		break;
200 	default:
201 		break;
202 	}
203 
204 	return 0;
205 }
206 
207 static const struct hwmon_ops dasharo_hwmon_ops = {
208 	.is_visible = dasharo_hwmon_is_visible,
209 	.read_string = dasharo_hwmon_read_string,
210 	.read = dasharo_hwmon_read,
211 };
212 
213 // Max 24 capabilities per feature
214 static const struct hwmon_channel_info * const dasharo_hwmon_info[] = {
215 	HWMON_CHANNEL_INFO(fan,
216 		HWMON_F_INPUT | HWMON_F_LABEL,
217 		HWMON_F_INPUT | HWMON_F_LABEL,
218 		HWMON_F_INPUT | HWMON_F_LABEL,
219 		HWMON_F_INPUT | HWMON_F_LABEL,
220 		HWMON_F_INPUT | HWMON_F_LABEL,
221 		HWMON_F_INPUT | HWMON_F_LABEL,
222 		HWMON_F_INPUT | HWMON_F_LABEL,
223 		HWMON_F_INPUT | HWMON_F_LABEL,
224 		HWMON_F_INPUT | HWMON_F_LABEL,
225 		HWMON_F_INPUT | HWMON_F_LABEL,
226 		HWMON_F_INPUT | HWMON_F_LABEL,
227 		HWMON_F_INPUT | HWMON_F_LABEL,
228 		HWMON_F_INPUT | HWMON_F_LABEL,
229 		HWMON_F_INPUT | HWMON_F_LABEL,
230 		HWMON_F_INPUT | HWMON_F_LABEL,
231 		HWMON_F_INPUT | HWMON_F_LABEL,
232 		HWMON_F_INPUT | HWMON_F_LABEL,
233 		HWMON_F_INPUT | HWMON_F_LABEL,
234 		HWMON_F_INPUT | HWMON_F_LABEL,
235 		HWMON_F_INPUT | HWMON_F_LABEL,
236 		HWMON_F_INPUT | HWMON_F_LABEL,
237 		HWMON_F_INPUT | HWMON_F_LABEL,
238 		HWMON_F_INPUT | HWMON_F_LABEL,
239 		HWMON_F_INPUT | HWMON_F_LABEL),
240 	HWMON_CHANNEL_INFO(temp,
241 		HWMON_T_INPUT | HWMON_T_LABEL,
242 		HWMON_T_INPUT | HWMON_T_LABEL,
243 		HWMON_T_INPUT | HWMON_T_LABEL,
244 		HWMON_T_INPUT | HWMON_T_LABEL,
245 		HWMON_T_INPUT | HWMON_T_LABEL,
246 		HWMON_T_INPUT | HWMON_T_LABEL,
247 		HWMON_T_INPUT | HWMON_T_LABEL,
248 		HWMON_T_INPUT | HWMON_T_LABEL,
249 		HWMON_T_INPUT | HWMON_T_LABEL,
250 		HWMON_T_INPUT | HWMON_T_LABEL,
251 		HWMON_T_INPUT | HWMON_T_LABEL,
252 		HWMON_T_INPUT | HWMON_T_LABEL,
253 		HWMON_T_INPUT | HWMON_T_LABEL,
254 		HWMON_T_INPUT | HWMON_T_LABEL,
255 		HWMON_T_INPUT | HWMON_T_LABEL,
256 		HWMON_T_INPUT | HWMON_T_LABEL,
257 		HWMON_T_INPUT | HWMON_T_LABEL,
258 		HWMON_T_INPUT | HWMON_T_LABEL,
259 		HWMON_T_INPUT | HWMON_T_LABEL,
260 		HWMON_T_INPUT | HWMON_T_LABEL,
261 		HWMON_T_INPUT | HWMON_T_LABEL,
262 		HWMON_T_INPUT | HWMON_T_LABEL,
263 		HWMON_T_INPUT | HWMON_T_LABEL,
264 		HWMON_T_INPUT | HWMON_T_LABEL),
265 	HWMON_CHANNEL_INFO(pwm,
266 		HWMON_PWM_INPUT,
267 		HWMON_PWM_INPUT,
268 		HWMON_PWM_INPUT,
269 		HWMON_PWM_INPUT,
270 		HWMON_PWM_INPUT,
271 		HWMON_PWM_INPUT,
272 		HWMON_PWM_INPUT,
273 		HWMON_PWM_INPUT,
274 		HWMON_PWM_INPUT,
275 		HWMON_PWM_INPUT,
276 		HWMON_PWM_INPUT,
277 		HWMON_PWM_INPUT,
278 		HWMON_PWM_INPUT,
279 		HWMON_PWM_INPUT,
280 		HWMON_PWM_INPUT,
281 		HWMON_PWM_INPUT,
282 		HWMON_PWM_INPUT,
283 		HWMON_PWM_INPUT,
284 		HWMON_PWM_INPUT,
285 		HWMON_PWM_INPUT,
286 		HWMON_PWM_INPUT,
287 		HWMON_PWM_INPUT,
288 		HWMON_PWM_INPUT,
289 		HWMON_PWM_INPUT),
290 	NULL
291 };
292 
293 static const struct hwmon_chip_info dasharo_hwmon_chip_info = {
294 	.ops = &dasharo_hwmon_ops,
295 	.info = dasharo_hwmon_info,
296 };
297 
dasharo_fill_feature_caps(struct dasharo_data * data,enum dasharo_feature feat)298 static void dasharo_fill_feature_caps(struct dasharo_data *data, enum dasharo_feature feat)
299 {
300 	struct dasharo_capability *cap;
301 	int cap_count = 0;
302 	int count;
303 
304 	for (int group = 0; group < MAX_GROUPS_PER_FEAT; ++group) {
305 		count = dasharo_get_feature_cap_count(data, feat, group);
306 		if (count <= 0)
307 			continue;
308 
309 		for (unsigned int i = 0; i < count; ++i) {
310 			if (cap_count >= ARRAY_SIZE(data->capabilities[feat]))
311 				break;
312 
313 			cap = &data->capabilities[feat][cap_count];
314 			cap->group = group;
315 			cap->index = i;
316 			scnprintf(cap->name, sizeof(cap->name), "%s %d",
317 				  dasharo_group_names[feat][group], i);
318 			cap_count++;
319 		}
320 	}
321 	data->caps_found[feat] = cap_count;
322 }
323 
dasharo_probe(struct platform_device * pdev)324 static int dasharo_probe(struct platform_device *pdev)
325 {
326 	struct dasharo_data *data;
327 	struct device *hwmon;
328 
329 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
330 	if (!data)
331 		return -ENOMEM;
332 	data->pdev = pdev;
333 
334 	for (unsigned int i = 0; i < DASHARO_FEATURE_MAX; ++i)
335 		dasharo_fill_feature_caps(data, i);
336 
337 	hwmon = devm_hwmon_device_register_with_info(&pdev->dev, "dasharo_acpi", data,
338 						     &dasharo_hwmon_chip_info, NULL);
339 
340 	return PTR_ERR_OR_ZERO(hwmon);
341 }
342 
343 static const struct acpi_device_id dasharo_device_ids[] = {
344 	{"DSHR0001", 0},
345 	{}
346 };
347 MODULE_DEVICE_TABLE(acpi, dasharo_device_ids);
348 
349 static struct platform_driver dasharo_driver = {
350 	.driver = {
351 		.name = "dasharo-acpi",
352 		.acpi_match_table = dasharo_device_ids,
353 	},
354 	.probe = dasharo_probe,
355 };
356 module_platform_driver(dasharo_driver);
357 
358 MODULE_DESCRIPTION("Dasharo ACPI Driver");
359 MODULE_AUTHOR("Michał Kopeć <michal.kopec@3mdeb.com>");
360 MODULE_LICENSE("GPL");
361