1*00ae053aSSrinivas Pandruvada // SPDX-License-Identifier: GPL-2.0-or-later 2*00ae053aSSrinivas Pandruvada /* 3*00ae053aSSrinivas Pandruvada * fan_core.c - ACPI Fan core Driver 4*00ae053aSSrinivas Pandruvada * 5*00ae053aSSrinivas Pandruvada * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 6*00ae053aSSrinivas Pandruvada * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 7*00ae053aSSrinivas Pandruvada * Copyright (C) 2022 Intel Corporation. All rights reserved. 8*00ae053aSSrinivas Pandruvada */ 9*00ae053aSSrinivas Pandruvada 10*00ae053aSSrinivas Pandruvada #include <linux/kernel.h> 11*00ae053aSSrinivas Pandruvada #include <linux/module.h> 12*00ae053aSSrinivas Pandruvada #include <linux/init.h> 13*00ae053aSSrinivas Pandruvada #include <linux/types.h> 14*00ae053aSSrinivas Pandruvada #include <linux/uaccess.h> 15*00ae053aSSrinivas Pandruvada #include <linux/thermal.h> 16*00ae053aSSrinivas Pandruvada #include <linux/acpi.h> 17*00ae053aSSrinivas Pandruvada #include <linux/platform_device.h> 18*00ae053aSSrinivas Pandruvada #include <linux/sort.h> 19*00ae053aSSrinivas Pandruvada 20*00ae053aSSrinivas Pandruvada #include "fan.h" 21*00ae053aSSrinivas Pandruvada 22*00ae053aSSrinivas Pandruvada MODULE_AUTHOR("Paul Diefenbaugh"); 23*00ae053aSSrinivas Pandruvada MODULE_DESCRIPTION("ACPI Fan Driver"); 24*00ae053aSSrinivas Pandruvada MODULE_LICENSE("GPL"); 25*00ae053aSSrinivas Pandruvada 26*00ae053aSSrinivas Pandruvada static int acpi_fan_probe(struct platform_device *pdev); 27*00ae053aSSrinivas Pandruvada static int acpi_fan_remove(struct platform_device *pdev); 28*00ae053aSSrinivas Pandruvada 29*00ae053aSSrinivas Pandruvada static const struct acpi_device_id fan_device_ids[] = { 30*00ae053aSSrinivas Pandruvada ACPI_FAN_DEVICE_IDS, 31*00ae053aSSrinivas Pandruvada {"", 0}, 32*00ae053aSSrinivas Pandruvada }; 33*00ae053aSSrinivas Pandruvada MODULE_DEVICE_TABLE(acpi, fan_device_ids); 34*00ae053aSSrinivas Pandruvada 35*00ae053aSSrinivas Pandruvada #ifdef CONFIG_PM_SLEEP 36*00ae053aSSrinivas Pandruvada static int acpi_fan_suspend(struct device *dev); 37*00ae053aSSrinivas Pandruvada static int acpi_fan_resume(struct device *dev); 38*00ae053aSSrinivas Pandruvada static const struct dev_pm_ops acpi_fan_pm = { 39*00ae053aSSrinivas Pandruvada .resume = acpi_fan_resume, 40*00ae053aSSrinivas Pandruvada .freeze = acpi_fan_suspend, 41*00ae053aSSrinivas Pandruvada .thaw = acpi_fan_resume, 42*00ae053aSSrinivas Pandruvada .restore = acpi_fan_resume, 43*00ae053aSSrinivas Pandruvada }; 44*00ae053aSSrinivas Pandruvada #define FAN_PM_OPS_PTR (&acpi_fan_pm) 45*00ae053aSSrinivas Pandruvada #else 46*00ae053aSSrinivas Pandruvada #define FAN_PM_OPS_PTR NULL 47*00ae053aSSrinivas Pandruvada #endif 48*00ae053aSSrinivas Pandruvada 49*00ae053aSSrinivas Pandruvada static struct platform_driver acpi_fan_driver = { 50*00ae053aSSrinivas Pandruvada .probe = acpi_fan_probe, 51*00ae053aSSrinivas Pandruvada .remove = acpi_fan_remove, 52*00ae053aSSrinivas Pandruvada .driver = { 53*00ae053aSSrinivas Pandruvada .name = "acpi-fan", 54*00ae053aSSrinivas Pandruvada .acpi_match_table = fan_device_ids, 55*00ae053aSSrinivas Pandruvada .pm = FAN_PM_OPS_PTR, 56*00ae053aSSrinivas Pandruvada }, 57*00ae053aSSrinivas Pandruvada }; 58*00ae053aSSrinivas Pandruvada 59*00ae053aSSrinivas Pandruvada /* thermal cooling device callbacks */ 60*00ae053aSSrinivas Pandruvada static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long 61*00ae053aSSrinivas Pandruvada *state) 62*00ae053aSSrinivas Pandruvada { 63*00ae053aSSrinivas Pandruvada struct acpi_device *device = cdev->devdata; 64*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 65*00ae053aSSrinivas Pandruvada 66*00ae053aSSrinivas Pandruvada if (fan->acpi4) 67*00ae053aSSrinivas Pandruvada *state = fan->fps_count - 1; 68*00ae053aSSrinivas Pandruvada else 69*00ae053aSSrinivas Pandruvada *state = 1; 70*00ae053aSSrinivas Pandruvada return 0; 71*00ae053aSSrinivas Pandruvada } 72*00ae053aSSrinivas Pandruvada 73*00ae053aSSrinivas Pandruvada static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) 74*00ae053aSSrinivas Pandruvada { 75*00ae053aSSrinivas Pandruvada struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 76*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 77*00ae053aSSrinivas Pandruvada union acpi_object *obj; 78*00ae053aSSrinivas Pandruvada acpi_status status; 79*00ae053aSSrinivas Pandruvada int control, i; 80*00ae053aSSrinivas Pandruvada 81*00ae053aSSrinivas Pandruvada status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); 82*00ae053aSSrinivas Pandruvada if (ACPI_FAILURE(status)) { 83*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Get fan state failed\n"); 84*00ae053aSSrinivas Pandruvada return -ENODEV; 85*00ae053aSSrinivas Pandruvada } 86*00ae053aSSrinivas Pandruvada 87*00ae053aSSrinivas Pandruvada obj = buffer.pointer; 88*00ae053aSSrinivas Pandruvada if (!obj || obj->type != ACPI_TYPE_PACKAGE || 89*00ae053aSSrinivas Pandruvada obj->package.count != 3 || 90*00ae053aSSrinivas Pandruvada obj->package.elements[1].type != ACPI_TYPE_INTEGER) { 91*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Invalid _FST data\n"); 92*00ae053aSSrinivas Pandruvada status = -EINVAL; 93*00ae053aSSrinivas Pandruvada goto err; 94*00ae053aSSrinivas Pandruvada } 95*00ae053aSSrinivas Pandruvada 96*00ae053aSSrinivas Pandruvada control = obj->package.elements[1].integer.value; 97*00ae053aSSrinivas Pandruvada for (i = 0; i < fan->fps_count; i++) { 98*00ae053aSSrinivas Pandruvada /* 99*00ae053aSSrinivas Pandruvada * When Fine Grain Control is set, return the state 100*00ae053aSSrinivas Pandruvada * corresponding to maximum fan->fps[i].control 101*00ae053aSSrinivas Pandruvada * value compared to the current speed. Here the 102*00ae053aSSrinivas Pandruvada * fan->fps[] is sorted array with increasing speed. 103*00ae053aSSrinivas Pandruvada */ 104*00ae053aSSrinivas Pandruvada if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) { 105*00ae053aSSrinivas Pandruvada i = (i > 0) ? i - 1 : 0; 106*00ae053aSSrinivas Pandruvada break; 107*00ae053aSSrinivas Pandruvada } else if (control == fan->fps[i].control) { 108*00ae053aSSrinivas Pandruvada break; 109*00ae053aSSrinivas Pandruvada } 110*00ae053aSSrinivas Pandruvada } 111*00ae053aSSrinivas Pandruvada if (i == fan->fps_count) { 112*00ae053aSSrinivas Pandruvada dev_dbg(&device->dev, "Invalid control value returned\n"); 113*00ae053aSSrinivas Pandruvada status = -EINVAL; 114*00ae053aSSrinivas Pandruvada goto err; 115*00ae053aSSrinivas Pandruvada } 116*00ae053aSSrinivas Pandruvada 117*00ae053aSSrinivas Pandruvada *state = i; 118*00ae053aSSrinivas Pandruvada 119*00ae053aSSrinivas Pandruvada err: 120*00ae053aSSrinivas Pandruvada kfree(obj); 121*00ae053aSSrinivas Pandruvada return status; 122*00ae053aSSrinivas Pandruvada } 123*00ae053aSSrinivas Pandruvada 124*00ae053aSSrinivas Pandruvada static int fan_get_state(struct acpi_device *device, unsigned long *state) 125*00ae053aSSrinivas Pandruvada { 126*00ae053aSSrinivas Pandruvada int result; 127*00ae053aSSrinivas Pandruvada int acpi_state = ACPI_STATE_D0; 128*00ae053aSSrinivas Pandruvada 129*00ae053aSSrinivas Pandruvada result = acpi_device_update_power(device, &acpi_state); 130*00ae053aSSrinivas Pandruvada if (result) 131*00ae053aSSrinivas Pandruvada return result; 132*00ae053aSSrinivas Pandruvada 133*00ae053aSSrinivas Pandruvada *state = acpi_state == ACPI_STATE_D3_COLD 134*00ae053aSSrinivas Pandruvada || acpi_state == ACPI_STATE_D3_HOT ? 135*00ae053aSSrinivas Pandruvada 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1); 136*00ae053aSSrinivas Pandruvada return 0; 137*00ae053aSSrinivas Pandruvada } 138*00ae053aSSrinivas Pandruvada 139*00ae053aSSrinivas Pandruvada static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long 140*00ae053aSSrinivas Pandruvada *state) 141*00ae053aSSrinivas Pandruvada { 142*00ae053aSSrinivas Pandruvada struct acpi_device *device = cdev->devdata; 143*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 144*00ae053aSSrinivas Pandruvada 145*00ae053aSSrinivas Pandruvada if (fan->acpi4) 146*00ae053aSSrinivas Pandruvada return fan_get_state_acpi4(device, state); 147*00ae053aSSrinivas Pandruvada else 148*00ae053aSSrinivas Pandruvada return fan_get_state(device, state); 149*00ae053aSSrinivas Pandruvada } 150*00ae053aSSrinivas Pandruvada 151*00ae053aSSrinivas Pandruvada static int fan_set_state(struct acpi_device *device, unsigned long state) 152*00ae053aSSrinivas Pandruvada { 153*00ae053aSSrinivas Pandruvada if (state != 0 && state != 1) 154*00ae053aSSrinivas Pandruvada return -EINVAL; 155*00ae053aSSrinivas Pandruvada 156*00ae053aSSrinivas Pandruvada return acpi_device_set_power(device, 157*00ae053aSSrinivas Pandruvada state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); 158*00ae053aSSrinivas Pandruvada } 159*00ae053aSSrinivas Pandruvada 160*00ae053aSSrinivas Pandruvada static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) 161*00ae053aSSrinivas Pandruvada { 162*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 163*00ae053aSSrinivas Pandruvada acpi_status status; 164*00ae053aSSrinivas Pandruvada 165*00ae053aSSrinivas Pandruvada if (state >= fan->fps_count) 166*00ae053aSSrinivas Pandruvada return -EINVAL; 167*00ae053aSSrinivas Pandruvada 168*00ae053aSSrinivas Pandruvada status = acpi_execute_simple_method(device->handle, "_FSL", 169*00ae053aSSrinivas Pandruvada fan->fps[state].control); 170*00ae053aSSrinivas Pandruvada if (ACPI_FAILURE(status)) { 171*00ae053aSSrinivas Pandruvada dev_dbg(&device->dev, "Failed to set state by _FSL\n"); 172*00ae053aSSrinivas Pandruvada return -ENODEV; 173*00ae053aSSrinivas Pandruvada } 174*00ae053aSSrinivas Pandruvada 175*00ae053aSSrinivas Pandruvada return 0; 176*00ae053aSSrinivas Pandruvada } 177*00ae053aSSrinivas Pandruvada 178*00ae053aSSrinivas Pandruvada static int 179*00ae053aSSrinivas Pandruvada fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) 180*00ae053aSSrinivas Pandruvada { 181*00ae053aSSrinivas Pandruvada struct acpi_device *device = cdev->devdata; 182*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 183*00ae053aSSrinivas Pandruvada 184*00ae053aSSrinivas Pandruvada if (fan->acpi4) 185*00ae053aSSrinivas Pandruvada return fan_set_state_acpi4(device, state); 186*00ae053aSSrinivas Pandruvada else 187*00ae053aSSrinivas Pandruvada return fan_set_state(device, state); 188*00ae053aSSrinivas Pandruvada } 189*00ae053aSSrinivas Pandruvada 190*00ae053aSSrinivas Pandruvada static const struct thermal_cooling_device_ops fan_cooling_ops = { 191*00ae053aSSrinivas Pandruvada .get_max_state = fan_get_max_state, 192*00ae053aSSrinivas Pandruvada .get_cur_state = fan_get_cur_state, 193*00ae053aSSrinivas Pandruvada .set_cur_state = fan_set_cur_state, 194*00ae053aSSrinivas Pandruvada }; 195*00ae053aSSrinivas Pandruvada 196*00ae053aSSrinivas Pandruvada /* -------------------------------------------------------------------------- 197*00ae053aSSrinivas Pandruvada * Driver Interface 198*00ae053aSSrinivas Pandruvada * -------------------------------------------------------------------------- 199*00ae053aSSrinivas Pandruvada */ 200*00ae053aSSrinivas Pandruvada 201*00ae053aSSrinivas Pandruvada static bool acpi_fan_is_acpi4(struct acpi_device *device) 202*00ae053aSSrinivas Pandruvada { 203*00ae053aSSrinivas Pandruvada return acpi_has_method(device->handle, "_FIF") && 204*00ae053aSSrinivas Pandruvada acpi_has_method(device->handle, "_FPS") && 205*00ae053aSSrinivas Pandruvada acpi_has_method(device->handle, "_FSL") && 206*00ae053aSSrinivas Pandruvada acpi_has_method(device->handle, "_FST"); 207*00ae053aSSrinivas Pandruvada } 208*00ae053aSSrinivas Pandruvada 209*00ae053aSSrinivas Pandruvada static int acpi_fan_get_fif(struct acpi_device *device) 210*00ae053aSSrinivas Pandruvada { 211*00ae053aSSrinivas Pandruvada struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 212*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 213*00ae053aSSrinivas Pandruvada struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; 214*00ae053aSSrinivas Pandruvada struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; 215*00ae053aSSrinivas Pandruvada union acpi_object *obj; 216*00ae053aSSrinivas Pandruvada acpi_status status; 217*00ae053aSSrinivas Pandruvada 218*00ae053aSSrinivas Pandruvada status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer); 219*00ae053aSSrinivas Pandruvada if (ACPI_FAILURE(status)) 220*00ae053aSSrinivas Pandruvada return status; 221*00ae053aSSrinivas Pandruvada 222*00ae053aSSrinivas Pandruvada obj = buffer.pointer; 223*00ae053aSSrinivas Pandruvada if (!obj || obj->type != ACPI_TYPE_PACKAGE) { 224*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Invalid _FIF data\n"); 225*00ae053aSSrinivas Pandruvada status = -EINVAL; 226*00ae053aSSrinivas Pandruvada goto err; 227*00ae053aSSrinivas Pandruvada } 228*00ae053aSSrinivas Pandruvada 229*00ae053aSSrinivas Pandruvada status = acpi_extract_package(obj, &format, &fif); 230*00ae053aSSrinivas Pandruvada if (ACPI_FAILURE(status)) { 231*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Invalid _FIF element\n"); 232*00ae053aSSrinivas Pandruvada status = -EINVAL; 233*00ae053aSSrinivas Pandruvada } 234*00ae053aSSrinivas Pandruvada 235*00ae053aSSrinivas Pandruvada err: 236*00ae053aSSrinivas Pandruvada kfree(obj); 237*00ae053aSSrinivas Pandruvada return status; 238*00ae053aSSrinivas Pandruvada } 239*00ae053aSSrinivas Pandruvada 240*00ae053aSSrinivas Pandruvada static int acpi_fan_speed_cmp(const void *a, const void *b) 241*00ae053aSSrinivas Pandruvada { 242*00ae053aSSrinivas Pandruvada const struct acpi_fan_fps *fps1 = a; 243*00ae053aSSrinivas Pandruvada const struct acpi_fan_fps *fps2 = b; 244*00ae053aSSrinivas Pandruvada return fps1->speed - fps2->speed; 245*00ae053aSSrinivas Pandruvada } 246*00ae053aSSrinivas Pandruvada 247*00ae053aSSrinivas Pandruvada static int acpi_fan_get_fps(struct acpi_device *device) 248*00ae053aSSrinivas Pandruvada { 249*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = acpi_driver_data(device); 250*00ae053aSSrinivas Pandruvada struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 251*00ae053aSSrinivas Pandruvada union acpi_object *obj; 252*00ae053aSSrinivas Pandruvada acpi_status status; 253*00ae053aSSrinivas Pandruvada int i; 254*00ae053aSSrinivas Pandruvada 255*00ae053aSSrinivas Pandruvada status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer); 256*00ae053aSSrinivas Pandruvada if (ACPI_FAILURE(status)) 257*00ae053aSSrinivas Pandruvada return status; 258*00ae053aSSrinivas Pandruvada 259*00ae053aSSrinivas Pandruvada obj = buffer.pointer; 260*00ae053aSSrinivas Pandruvada if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) { 261*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Invalid _FPS data\n"); 262*00ae053aSSrinivas Pandruvada status = -EINVAL; 263*00ae053aSSrinivas Pandruvada goto err; 264*00ae053aSSrinivas Pandruvada } 265*00ae053aSSrinivas Pandruvada 266*00ae053aSSrinivas Pandruvada fan->fps_count = obj->package.count - 1; /* minus revision field */ 267*00ae053aSSrinivas Pandruvada fan->fps = devm_kcalloc(&device->dev, 268*00ae053aSSrinivas Pandruvada fan->fps_count, sizeof(struct acpi_fan_fps), 269*00ae053aSSrinivas Pandruvada GFP_KERNEL); 270*00ae053aSSrinivas Pandruvada if (!fan->fps) { 271*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Not enough memory\n"); 272*00ae053aSSrinivas Pandruvada status = -ENOMEM; 273*00ae053aSSrinivas Pandruvada goto err; 274*00ae053aSSrinivas Pandruvada } 275*00ae053aSSrinivas Pandruvada for (i = 0; i < fan->fps_count; i++) { 276*00ae053aSSrinivas Pandruvada struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; 277*00ae053aSSrinivas Pandruvada struct acpi_buffer fps = { offsetof(struct acpi_fan_fps, name), 278*00ae053aSSrinivas Pandruvada &fan->fps[i] }; 279*00ae053aSSrinivas Pandruvada status = acpi_extract_package(&obj->package.elements[i + 1], 280*00ae053aSSrinivas Pandruvada &format, &fps); 281*00ae053aSSrinivas Pandruvada if (ACPI_FAILURE(status)) { 282*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Invalid _FPS element\n"); 283*00ae053aSSrinivas Pandruvada goto err; 284*00ae053aSSrinivas Pandruvada } 285*00ae053aSSrinivas Pandruvada } 286*00ae053aSSrinivas Pandruvada 287*00ae053aSSrinivas Pandruvada /* sort the state array according to fan speed in increase order */ 288*00ae053aSSrinivas Pandruvada sort(fan->fps, fan->fps_count, sizeof(*fan->fps), 289*00ae053aSSrinivas Pandruvada acpi_fan_speed_cmp, NULL); 290*00ae053aSSrinivas Pandruvada 291*00ae053aSSrinivas Pandruvada err: 292*00ae053aSSrinivas Pandruvada kfree(obj); 293*00ae053aSSrinivas Pandruvada return status; 294*00ae053aSSrinivas Pandruvada } 295*00ae053aSSrinivas Pandruvada 296*00ae053aSSrinivas Pandruvada static int acpi_fan_probe(struct platform_device *pdev) 297*00ae053aSSrinivas Pandruvada { 298*00ae053aSSrinivas Pandruvada int result = 0; 299*00ae053aSSrinivas Pandruvada struct thermal_cooling_device *cdev; 300*00ae053aSSrinivas Pandruvada struct acpi_fan *fan; 301*00ae053aSSrinivas Pandruvada struct acpi_device *device = ACPI_COMPANION(&pdev->dev); 302*00ae053aSSrinivas Pandruvada char *name; 303*00ae053aSSrinivas Pandruvada 304*00ae053aSSrinivas Pandruvada fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); 305*00ae053aSSrinivas Pandruvada if (!fan) { 306*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "No memory for fan\n"); 307*00ae053aSSrinivas Pandruvada return -ENOMEM; 308*00ae053aSSrinivas Pandruvada } 309*00ae053aSSrinivas Pandruvada device->driver_data = fan; 310*00ae053aSSrinivas Pandruvada platform_set_drvdata(pdev, fan); 311*00ae053aSSrinivas Pandruvada 312*00ae053aSSrinivas Pandruvada if (acpi_fan_is_acpi4(device)) { 313*00ae053aSSrinivas Pandruvada result = acpi_fan_get_fif(device); 314*00ae053aSSrinivas Pandruvada if (result) 315*00ae053aSSrinivas Pandruvada return result; 316*00ae053aSSrinivas Pandruvada 317*00ae053aSSrinivas Pandruvada result = acpi_fan_get_fps(device); 318*00ae053aSSrinivas Pandruvada if (result) 319*00ae053aSSrinivas Pandruvada return result; 320*00ae053aSSrinivas Pandruvada 321*00ae053aSSrinivas Pandruvada result = acpi_fan_create_attributes(device); 322*00ae053aSSrinivas Pandruvada if (result) 323*00ae053aSSrinivas Pandruvada return result; 324*00ae053aSSrinivas Pandruvada 325*00ae053aSSrinivas Pandruvada fan->acpi4 = true; 326*00ae053aSSrinivas Pandruvada } else { 327*00ae053aSSrinivas Pandruvada result = acpi_device_update_power(device, NULL); 328*00ae053aSSrinivas Pandruvada if (result) { 329*00ae053aSSrinivas Pandruvada dev_err(&device->dev, "Failed to set initial power state\n"); 330*00ae053aSSrinivas Pandruvada goto err_end; 331*00ae053aSSrinivas Pandruvada } 332*00ae053aSSrinivas Pandruvada } 333*00ae053aSSrinivas Pandruvada 334*00ae053aSSrinivas Pandruvada if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B"))) 335*00ae053aSSrinivas Pandruvada name = "Fan"; 336*00ae053aSSrinivas Pandruvada else 337*00ae053aSSrinivas Pandruvada name = acpi_device_bid(device); 338*00ae053aSSrinivas Pandruvada 339*00ae053aSSrinivas Pandruvada cdev = thermal_cooling_device_register(name, device, 340*00ae053aSSrinivas Pandruvada &fan_cooling_ops); 341*00ae053aSSrinivas Pandruvada if (IS_ERR(cdev)) { 342*00ae053aSSrinivas Pandruvada result = PTR_ERR(cdev); 343*00ae053aSSrinivas Pandruvada goto err_end; 344*00ae053aSSrinivas Pandruvada } 345*00ae053aSSrinivas Pandruvada 346*00ae053aSSrinivas Pandruvada dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); 347*00ae053aSSrinivas Pandruvada 348*00ae053aSSrinivas Pandruvada fan->cdev = cdev; 349*00ae053aSSrinivas Pandruvada result = sysfs_create_link(&pdev->dev.kobj, 350*00ae053aSSrinivas Pandruvada &cdev->device.kobj, 351*00ae053aSSrinivas Pandruvada "thermal_cooling"); 352*00ae053aSSrinivas Pandruvada if (result) 353*00ae053aSSrinivas Pandruvada dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n"); 354*00ae053aSSrinivas Pandruvada 355*00ae053aSSrinivas Pandruvada result = sysfs_create_link(&cdev->device.kobj, 356*00ae053aSSrinivas Pandruvada &pdev->dev.kobj, 357*00ae053aSSrinivas Pandruvada "device"); 358*00ae053aSSrinivas Pandruvada if (result) { 359*00ae053aSSrinivas Pandruvada dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n"); 360*00ae053aSSrinivas Pandruvada goto err_end; 361*00ae053aSSrinivas Pandruvada } 362*00ae053aSSrinivas Pandruvada 363*00ae053aSSrinivas Pandruvada return 0; 364*00ae053aSSrinivas Pandruvada 365*00ae053aSSrinivas Pandruvada err_end: 366*00ae053aSSrinivas Pandruvada if (fan->acpi4) 367*00ae053aSSrinivas Pandruvada acpi_fan_delete_attributes(device); 368*00ae053aSSrinivas Pandruvada 369*00ae053aSSrinivas Pandruvada return result; 370*00ae053aSSrinivas Pandruvada } 371*00ae053aSSrinivas Pandruvada 372*00ae053aSSrinivas Pandruvada static int acpi_fan_remove(struct platform_device *pdev) 373*00ae053aSSrinivas Pandruvada { 374*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = platform_get_drvdata(pdev); 375*00ae053aSSrinivas Pandruvada 376*00ae053aSSrinivas Pandruvada if (fan->acpi4) { 377*00ae053aSSrinivas Pandruvada struct acpi_device *device = ACPI_COMPANION(&pdev->dev); 378*00ae053aSSrinivas Pandruvada 379*00ae053aSSrinivas Pandruvada acpi_fan_delete_attributes(device); 380*00ae053aSSrinivas Pandruvada } 381*00ae053aSSrinivas Pandruvada sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); 382*00ae053aSSrinivas Pandruvada sysfs_remove_link(&fan->cdev->device.kobj, "device"); 383*00ae053aSSrinivas Pandruvada thermal_cooling_device_unregister(fan->cdev); 384*00ae053aSSrinivas Pandruvada 385*00ae053aSSrinivas Pandruvada return 0; 386*00ae053aSSrinivas Pandruvada } 387*00ae053aSSrinivas Pandruvada 388*00ae053aSSrinivas Pandruvada #ifdef CONFIG_PM_SLEEP 389*00ae053aSSrinivas Pandruvada static int acpi_fan_suspend(struct device *dev) 390*00ae053aSSrinivas Pandruvada { 391*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = dev_get_drvdata(dev); 392*00ae053aSSrinivas Pandruvada if (fan->acpi4) 393*00ae053aSSrinivas Pandruvada return 0; 394*00ae053aSSrinivas Pandruvada 395*00ae053aSSrinivas Pandruvada acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0); 396*00ae053aSSrinivas Pandruvada 397*00ae053aSSrinivas Pandruvada return AE_OK; 398*00ae053aSSrinivas Pandruvada } 399*00ae053aSSrinivas Pandruvada 400*00ae053aSSrinivas Pandruvada static int acpi_fan_resume(struct device *dev) 401*00ae053aSSrinivas Pandruvada { 402*00ae053aSSrinivas Pandruvada int result; 403*00ae053aSSrinivas Pandruvada struct acpi_fan *fan = dev_get_drvdata(dev); 404*00ae053aSSrinivas Pandruvada 405*00ae053aSSrinivas Pandruvada if (fan->acpi4) 406*00ae053aSSrinivas Pandruvada return 0; 407*00ae053aSSrinivas Pandruvada 408*00ae053aSSrinivas Pandruvada result = acpi_device_update_power(ACPI_COMPANION(dev), NULL); 409*00ae053aSSrinivas Pandruvada if (result) 410*00ae053aSSrinivas Pandruvada dev_err(dev, "Error updating fan power state\n"); 411*00ae053aSSrinivas Pandruvada 412*00ae053aSSrinivas Pandruvada return result; 413*00ae053aSSrinivas Pandruvada } 414*00ae053aSSrinivas Pandruvada #endif 415*00ae053aSSrinivas Pandruvada 416*00ae053aSSrinivas Pandruvada module_platform_driver(acpi_fan_driver); 417