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