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 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 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 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 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 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 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 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