blob: 8a5b450576dfc2ff38a035e3deda59d9c972f50c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * acpi_fan.c - ACPI Fan Driver ($Revision: 29 $)
3 *
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6 *
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <asm/uaccess.h>
Zhang Rui05a83d92008-01-17 15:51:24 +080031#include <linux/thermal.h>
Lv Zheng8b484632013-12-03 08:49:16 +080032#include <linux/acpi.h>
Aaron Lu19593a12013-11-19 16:59:20 +080033#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Len Brownf52fd662007-02-12 22:42:12 -050035MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050036MODULE_DESCRIPTION("ACPI Fan Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070037MODULE_LICENSE("GPL");
38
Aaron Lu19593a12013-11-19 16:59:20 +080039static int acpi_fan_probe(struct platform_device *pdev);
40static int acpi_fan_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
Thomas Renninger1ba90e32007-07-23 14:44:41 +020042static const struct acpi_device_id fan_device_ids[] = {
43 {"PNP0C0B", 0},
44 {"", 0},
45};
46MODULE_DEVICE_TABLE(acpi, fan_device_ids);
47
Rafael J. Wysocki90692402012-08-09 23:00:02 +020048#ifdef CONFIG_PM_SLEEP
Rafael J. Wysocki62fcbdd2012-06-27 23:26:07 +020049static int acpi_fan_suspend(struct device *dev);
50static int acpi_fan_resume(struct device *dev);
Aaron Lub9b85152014-02-19 12:16:48 +080051static struct dev_pm_ops acpi_fan_pm = {
52 .resume = acpi_fan_resume,
53 .freeze = acpi_fan_suspend,
54 .thaw = acpi_fan_resume,
55 .restore = acpi_fan_resume,
56};
57#define FAN_PM_OPS_PTR (&acpi_fan_pm)
Shuah Khanb108e0e2014-02-12 20:19:08 -070058#else
Aaron Lub9b85152014-02-19 12:16:48 +080059#define FAN_PM_OPS_PTR NULL
Rafael J. Wysocki90692402012-08-09 23:00:02 +020060#endif
Rafael J. Wysocki62fcbdd2012-06-27 23:26:07 +020061
Aaron Lu19593a12013-11-19 16:59:20 +080062static struct platform_driver acpi_fan_driver = {
63 .probe = acpi_fan_probe,
64 .remove = acpi_fan_remove,
65 .driver = {
66 .name = "acpi-fan",
67 .acpi_match_table = fan_device_ids,
68 .pm = FAN_PM_OPS_PTR,
69 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070070};
71
Zhang Rui05a83d92008-01-17 15:51:24 +080072/* thermal cooling device callbacks */
Matthew Garrett6503e5d2008-11-27 17:48:13 +000073static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
74 *state)
Zhang Rui05a83d92008-01-17 15:51:24 +080075{
76 /* ACPI fan device only support two states: ON/OFF */
Matthew Garrett6503e5d2008-11-27 17:48:13 +000077 *state = 1;
78 return 0;
Zhang Rui05a83d92008-01-17 15:51:24 +080079}
80
Matthew Garrett6503e5d2008-11-27 17:48:13 +000081static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
82 *state)
Zhang Rui05a83d92008-01-17 15:51:24 +080083{
84 struct acpi_device *device = cdev->devdata;
Zhang Rui05a83d92008-01-17 15:51:24 +080085 int result;
Naresh Bhat85eb9822013-07-01 19:51:00 +053086 int acpi_state = ACPI_STATE_D0;
Zhang Rui05a83d92008-01-17 15:51:24 +080087
88 if (!device)
89 return -EINVAL;
90
Aaron Lu2bb3a2b2013-11-19 15:43:52 +080091 result = acpi_device_update_power(device, &acpi_state);
Zhang Rui05a83d92008-01-17 15:51:24 +080092 if (result)
93 return result;
94
Rafael J. Wysocki8ad928d2013-07-30 14:36:20 +020095 *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 :
Matthew Garrett6503e5d2008-11-27 17:48:13 +000096 (acpi_state == ACPI_STATE_D0 ? 1 : -1));
97 return 0;
Zhang Rui05a83d92008-01-17 15:51:24 +080098}
99
100static int
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000101fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
Zhang Rui05a83d92008-01-17 15:51:24 +0800102{
103 struct acpi_device *device = cdev->devdata;
104 int result;
105
106 if (!device || (state != 0 && state != 1))
107 return -EINVAL;
108
Aaron Lu2bb3a2b2013-11-19 15:43:52 +0800109 result = acpi_device_set_power(device,
Rafael J. Wysocki8ad928d2013-07-30 14:36:20 +0200110 state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
Zhang Rui05a83d92008-01-17 15:51:24 +0800111
112 return result;
113}
114
Vasiliy Kulikov9c8b04b2011-06-25 21:07:52 +0400115static const struct thermal_cooling_device_ops fan_cooling_ops = {
Zhang Rui05a83d92008-01-17 15:51:24 +0800116 .get_max_state = fan_get_max_state,
117 .get_cur_state = fan_get_cur_state,
118 .set_cur_state = fan_set_cur_state,
119};
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/* --------------------------------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 Driver Interface
123 -------------------------------------------------------------------------- */
124
Aaron Lu19593a12013-11-19 16:59:20 +0800125static int acpi_fan_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Len Brown4be44fc2005-08-05 00:44:28 -0400127 int result = 0;
Zhang Rui05a83d92008-01-17 15:51:24 +0800128 struct thermal_cooling_device *cdev;
Aaron Lu19593a12013-11-19 16:59:20 +0800129 struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
Aaron Lu2bb3a2b2013-11-19 15:43:52 +0800131 result = acpi_device_update_power(device, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 if (result) {
Aaron Lu19593a12013-11-19 16:59:20 +0800133 dev_err(&pdev->dev, "Setting initial power state\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 goto end;
135 }
136
Zhang Rui05a83d92008-01-17 15:51:24 +0800137 cdev = thermal_cooling_device_register("Fan", device,
138 &fan_cooling_ops);
Thomas Sujith19b36782008-02-15 01:01:52 -0500139 if (IS_ERR(cdev)) {
140 result = PTR_ERR(cdev);
141 goto end;
142 }
Zhang Rui05a83d92008-01-17 15:51:24 +0800143
Aaron Lu19593a12013-11-19 16:59:20 +0800144 dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
Thomas Sujith19b36782008-02-15 01:01:52 -0500145
Aaron Lu19593a12013-11-19 16:59:20 +0800146 platform_set_drvdata(pdev, cdev);
147 result = sysfs_create_link(&pdev->dev.kobj,
Julia Lawall90300622008-04-11 10:09:24 +0800148 &cdev->device.kobj,
149 "thermal_cooling");
150 if (result)
Aaron Lu19593a12013-11-19 16:59:20 +0800151 dev_err(&pdev->dev, "Failed to create sysfs link "
Greg Kroah-Hartmanfc3a8822008-05-02 06:02:41 +0200152 "'thermal_cooling'\n");
Julia Lawall90300622008-04-11 10:09:24 +0800153
154 result = sysfs_create_link(&cdev->device.kobj,
Aaron Lu19593a12013-11-19 16:59:20 +0800155 &pdev->dev.kobj,
Julia Lawall90300622008-04-11 10:09:24 +0800156 "device");
157 if (result)
Aaron Lu19593a12013-11-19 16:59:20 +0800158 dev_err(&pdev->dev, "Failed to create sysfs link "
Greg Kroah-Hartmanfc3a8822008-05-02 06:02:41 +0200159 "'device'\n");
Zhang Rui05a83d92008-01-17 15:51:24 +0800160
Aaron Lu19593a12013-11-19 16:59:20 +0800161 dev_info(&pdev->dev, "%s [%s] (%s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400162 acpi_device_name(device), acpi_device_bid(device),
163 !device->power.state ? "on" : "off");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
Felipe Contreras568b6ad2013-08-30 16:16:05 -0500165end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400166 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167}
168
Aaron Lu19593a12013-11-19 16:59:20 +0800169static int acpi_fan_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
Aaron Lu19593a12013-11-19 16:59:20 +0800171 struct thermal_cooling_device *cdev = platform_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Aaron Lu19593a12013-11-19 16:59:20 +0800173 sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
Zhang Rui05a83d92008-01-17 15:51:24 +0800174 sysfs_remove_link(&cdev->device.kobj, "device");
175 thermal_cooling_device_unregister(cdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Patrick Mocheld550d982006-06-27 00:41:40 -0400177 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178}
179
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200180#ifdef CONFIG_PM_SLEEP
Rafael J. Wysocki62fcbdd2012-06-27 23:26:07 +0200181static int acpi_fan_suspend(struct device *dev)
Len Brownec683732008-01-23 22:41:20 -0500182{
Aaron Lu19593a12013-11-19 16:59:20 +0800183 acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
Len Brownec683732008-01-23 22:41:20 -0500184
185 return AE_OK;
186}
187
Rafael J. Wysocki62fcbdd2012-06-27 23:26:07 +0200188static int acpi_fan_resume(struct device *dev)
Len Brownec683732008-01-23 22:41:20 -0500189{
Rafael J. Wysocki488a76c2010-11-25 00:11:24 +0100190 int result;
Len Brownec683732008-01-23 22:41:20 -0500191
Aaron Lu19593a12013-11-19 16:59:20 +0800192 result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
Rafael J. Wysocki488a76c2010-11-25 00:11:24 +0100193 if (result)
Aaron Lu19593a12013-11-19 16:59:20 +0800194 dev_err(dev, "Error updating fan power state\n");
Len Brownec683732008-01-23 22:41:20 -0500195
196 return result;
197}
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200198#endif
Len Brownec683732008-01-23 22:41:20 -0500199
Aaron Lu19593a12013-11-19 16:59:20 +0800200module_platform_driver(acpi_fan_driver);