0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/init.h>
0013 #include <linux/types.h>
0014 #include <linux/uaccess.h>
0015 #include <linux/thermal.h>
0016 #include <linux/acpi.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/sort.h>
0019
0020 #include "fan.h"
0021
0022 MODULE_AUTHOR("Paul Diefenbaugh");
0023 MODULE_DESCRIPTION("ACPI Fan Driver");
0024 MODULE_LICENSE("GPL");
0025
0026 static int acpi_fan_probe(struct platform_device *pdev);
0027 static int acpi_fan_remove(struct platform_device *pdev);
0028
0029 static const struct acpi_device_id fan_device_ids[] = {
0030 ACPI_FAN_DEVICE_IDS,
0031 {"", 0},
0032 };
0033 MODULE_DEVICE_TABLE(acpi, fan_device_ids);
0034
0035 #ifdef CONFIG_PM_SLEEP
0036 static int acpi_fan_suspend(struct device *dev);
0037 static int acpi_fan_resume(struct device *dev);
0038 static const struct dev_pm_ops acpi_fan_pm = {
0039 .resume = acpi_fan_resume,
0040 .freeze = acpi_fan_suspend,
0041 .thaw = acpi_fan_resume,
0042 .restore = acpi_fan_resume,
0043 };
0044 #define FAN_PM_OPS_PTR (&acpi_fan_pm)
0045 #else
0046 #define FAN_PM_OPS_PTR NULL
0047 #endif
0048
0049 static struct platform_driver acpi_fan_driver = {
0050 .probe = acpi_fan_probe,
0051 .remove = acpi_fan_remove,
0052 .driver = {
0053 .name = "acpi-fan",
0054 .acpi_match_table = fan_device_ids,
0055 .pm = FAN_PM_OPS_PTR,
0056 },
0057 };
0058
0059
0060 static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
0061 *state)
0062 {
0063 struct acpi_device *device = cdev->devdata;
0064 struct acpi_fan *fan = acpi_driver_data(device);
0065
0066 if (fan->acpi4) {
0067 if (fan->fif.fine_grain_ctrl)
0068 *state = 100 / fan->fif.step_size;
0069 else
0070 *state = fan->fps_count - 1;
0071 } else {
0072 *state = 1;
0073 }
0074
0075 return 0;
0076 }
0077
0078 int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
0079 {
0080 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0081 union acpi_object *obj;
0082 acpi_status status;
0083 int ret = 0;
0084
0085 status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
0086 if (ACPI_FAILURE(status)) {
0087 dev_err(&device->dev, "Get fan state failed\n");
0088 return -ENODEV;
0089 }
0090
0091 obj = buffer.pointer;
0092 if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
0093 obj->package.count != 3 ||
0094 obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
0095 dev_err(&device->dev, "Invalid _FST data\n");
0096 ret = -EINVAL;
0097 goto err;
0098 }
0099
0100 fst->revision = obj->package.elements[0].integer.value;
0101 fst->control = obj->package.elements[1].integer.value;
0102 fst->speed = obj->package.elements[2].integer.value;
0103
0104 err:
0105 kfree(obj);
0106 return ret;
0107 }
0108
0109 static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
0110 {
0111 struct acpi_fan *fan = acpi_driver_data(device);
0112 struct acpi_fan_fst fst;
0113 int status, i;
0114
0115 status = acpi_fan_get_fst(device, &fst);
0116 if (status)
0117 return status;
0118
0119 if (fan->fif.fine_grain_ctrl) {
0120
0121 if (fst.control > 100) {
0122 dev_dbg(&device->dev, "Invalid control value returned\n");
0123 goto match_fps;
0124 }
0125
0126 *state = (int) fst.control / fan->fif.step_size;
0127 return 0;
0128 }
0129
0130 match_fps:
0131 for (i = 0; i < fan->fps_count; i++) {
0132 if (fst.control == fan->fps[i].control)
0133 break;
0134 }
0135 if (i == fan->fps_count) {
0136 dev_dbg(&device->dev, "Invalid control value returned\n");
0137 return -EINVAL;
0138 }
0139
0140 *state = i;
0141
0142 return status;
0143 }
0144
0145 static int fan_get_state(struct acpi_device *device, unsigned long *state)
0146 {
0147 int result;
0148 int acpi_state = ACPI_STATE_D0;
0149
0150 result = acpi_device_update_power(device, &acpi_state);
0151 if (result)
0152 return result;
0153
0154 *state = acpi_state == ACPI_STATE_D3_COLD
0155 || acpi_state == ACPI_STATE_D3_HOT ?
0156 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
0157 return 0;
0158 }
0159
0160 static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
0161 *state)
0162 {
0163 struct acpi_device *device = cdev->devdata;
0164 struct acpi_fan *fan = acpi_driver_data(device);
0165
0166 if (fan->acpi4)
0167 return fan_get_state_acpi4(device, state);
0168 else
0169 return fan_get_state(device, state);
0170 }
0171
0172 static int fan_set_state(struct acpi_device *device, unsigned long state)
0173 {
0174 if (state != 0 && state != 1)
0175 return -EINVAL;
0176
0177 return acpi_device_set_power(device,
0178 state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
0179 }
0180
0181 static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
0182 {
0183 struct acpi_fan *fan = acpi_driver_data(device);
0184 acpi_status status;
0185 u64 value = state;
0186 int max_state;
0187
0188 if (fan->fif.fine_grain_ctrl)
0189 max_state = 100 / fan->fif.step_size;
0190 else
0191 max_state = fan->fps_count - 1;
0192
0193 if (state > max_state)
0194 return -EINVAL;
0195
0196 if (fan->fif.fine_grain_ctrl) {
0197 value *= fan->fif.step_size;
0198
0199 if (value + fan->fif.step_size > 100)
0200 value = 100;
0201 } else {
0202 value = fan->fps[state].control;
0203 }
0204
0205 status = acpi_execute_simple_method(device->handle, "_FSL", value);
0206 if (ACPI_FAILURE(status)) {
0207 dev_dbg(&device->dev, "Failed to set state by _FSL\n");
0208 return -ENODEV;
0209 }
0210
0211 return 0;
0212 }
0213
0214 static int
0215 fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
0216 {
0217 struct acpi_device *device = cdev->devdata;
0218 struct acpi_fan *fan = acpi_driver_data(device);
0219
0220 if (fan->acpi4)
0221 return fan_set_state_acpi4(device, state);
0222 else
0223 return fan_set_state(device, state);
0224 }
0225
0226 static const struct thermal_cooling_device_ops fan_cooling_ops = {
0227 .get_max_state = fan_get_max_state,
0228 .get_cur_state = fan_get_cur_state,
0229 .set_cur_state = fan_set_cur_state,
0230 };
0231
0232
0233
0234
0235
0236
0237 static bool acpi_fan_is_acpi4(struct acpi_device *device)
0238 {
0239 return acpi_has_method(device->handle, "_FIF") &&
0240 acpi_has_method(device->handle, "_FPS") &&
0241 acpi_has_method(device->handle, "_FSL") &&
0242 acpi_has_method(device->handle, "_FST");
0243 }
0244
0245 static int acpi_fan_get_fif(struct acpi_device *device)
0246 {
0247 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0248 struct acpi_fan *fan = acpi_driver_data(device);
0249 struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
0250 u64 fields[4];
0251 struct acpi_buffer fif = { sizeof(fields), fields };
0252 union acpi_object *obj;
0253 acpi_status status;
0254
0255 status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
0256 if (ACPI_FAILURE(status))
0257 return status;
0258
0259 obj = buffer.pointer;
0260 if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
0261 dev_err(&device->dev, "Invalid _FIF data\n");
0262 status = -EINVAL;
0263 goto err;
0264 }
0265
0266 status = acpi_extract_package(obj, &format, &fif);
0267 if (ACPI_FAILURE(status)) {
0268 dev_err(&device->dev, "Invalid _FIF element\n");
0269 status = -EINVAL;
0270 }
0271
0272 fan->fif.revision = fields[0];
0273 fan->fif.fine_grain_ctrl = fields[1];
0274 fan->fif.step_size = fields[2];
0275 fan->fif.low_speed_notification = fields[3];
0276
0277
0278 if (!fan->fif.step_size)
0279 fan->fif.step_size = 1;
0280
0281 else if (fan->fif.step_size > 9)
0282 fan->fif.step_size = 9;
0283 err:
0284 kfree(obj);
0285 return status;
0286 }
0287
0288 static int acpi_fan_speed_cmp(const void *a, const void *b)
0289 {
0290 const struct acpi_fan_fps *fps1 = a;
0291 const struct acpi_fan_fps *fps2 = b;
0292 return fps1->speed - fps2->speed;
0293 }
0294
0295 static int acpi_fan_get_fps(struct acpi_device *device)
0296 {
0297 struct acpi_fan *fan = acpi_driver_data(device);
0298 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0299 union acpi_object *obj;
0300 acpi_status status;
0301 int i;
0302
0303 status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
0304 if (ACPI_FAILURE(status))
0305 return status;
0306
0307 obj = buffer.pointer;
0308 if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
0309 dev_err(&device->dev, "Invalid _FPS data\n");
0310 status = -EINVAL;
0311 goto err;
0312 }
0313
0314 fan->fps_count = obj->package.count - 1;
0315 fan->fps = devm_kcalloc(&device->dev,
0316 fan->fps_count, sizeof(struct acpi_fan_fps),
0317 GFP_KERNEL);
0318 if (!fan->fps) {
0319 dev_err(&device->dev, "Not enough memory\n");
0320 status = -ENOMEM;
0321 goto err;
0322 }
0323 for (i = 0; i < fan->fps_count; i++) {
0324 struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
0325 struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name),
0326 &fan->fps[i] };
0327 status = acpi_extract_package(&obj->package.elements[i + 1],
0328 &format, &fps);
0329 if (ACPI_FAILURE(status)) {
0330 dev_err(&device->dev, "Invalid _FPS element\n");
0331 goto err;
0332 }
0333 }
0334
0335
0336 sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
0337 acpi_fan_speed_cmp, NULL);
0338
0339 err:
0340 kfree(obj);
0341 return status;
0342 }
0343
0344 static int acpi_fan_probe(struct platform_device *pdev)
0345 {
0346 int result = 0;
0347 struct thermal_cooling_device *cdev;
0348 struct acpi_fan *fan;
0349 struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
0350 char *name;
0351
0352 fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
0353 if (!fan) {
0354 dev_err(&device->dev, "No memory for fan\n");
0355 return -ENOMEM;
0356 }
0357 device->driver_data = fan;
0358 platform_set_drvdata(pdev, fan);
0359
0360 if (acpi_fan_is_acpi4(device)) {
0361 result = acpi_fan_get_fif(device);
0362 if (result)
0363 return result;
0364
0365 result = acpi_fan_get_fps(device);
0366 if (result)
0367 return result;
0368
0369 result = acpi_fan_create_attributes(device);
0370 if (result)
0371 return result;
0372
0373 fan->acpi4 = true;
0374 } else {
0375 result = acpi_device_update_power(device, NULL);
0376 if (result) {
0377 dev_err(&device->dev, "Failed to set initial power state\n");
0378 goto err_end;
0379 }
0380 }
0381
0382 if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
0383 name = "Fan";
0384 else
0385 name = acpi_device_bid(device);
0386
0387 cdev = thermal_cooling_device_register(name, device,
0388 &fan_cooling_ops);
0389 if (IS_ERR(cdev)) {
0390 result = PTR_ERR(cdev);
0391 goto err_end;
0392 }
0393
0394 dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
0395
0396 fan->cdev = cdev;
0397 result = sysfs_create_link(&pdev->dev.kobj,
0398 &cdev->device.kobj,
0399 "thermal_cooling");
0400 if (result)
0401 dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
0402
0403 result = sysfs_create_link(&cdev->device.kobj,
0404 &pdev->dev.kobj,
0405 "device");
0406 if (result) {
0407 dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
0408 goto err_end;
0409 }
0410
0411 return 0;
0412
0413 err_end:
0414 if (fan->acpi4)
0415 acpi_fan_delete_attributes(device);
0416
0417 return result;
0418 }
0419
0420 static int acpi_fan_remove(struct platform_device *pdev)
0421 {
0422 struct acpi_fan *fan = platform_get_drvdata(pdev);
0423
0424 if (fan->acpi4) {
0425 struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
0426
0427 acpi_fan_delete_attributes(device);
0428 }
0429 sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
0430 sysfs_remove_link(&fan->cdev->device.kobj, "device");
0431 thermal_cooling_device_unregister(fan->cdev);
0432
0433 return 0;
0434 }
0435
0436 #ifdef CONFIG_PM_SLEEP
0437 static int acpi_fan_suspend(struct device *dev)
0438 {
0439 struct acpi_fan *fan = dev_get_drvdata(dev);
0440 if (fan->acpi4)
0441 return 0;
0442
0443 acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
0444
0445 return AE_OK;
0446 }
0447
0448 static int acpi_fan_resume(struct device *dev)
0449 {
0450 int result;
0451 struct acpi_fan *fan = dev_get_drvdata(dev);
0452
0453 if (fan->acpi4)
0454 return 0;
0455
0456 result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
0457 if (result)
0458 dev_err(dev, "Error updating fan power state\n");
0459
0460 return result;
0461 }
0462 #endif
0463
0464 module_platform_driver(acpi_fan_driver);