diff options
| author | Aaron Lu <aaron.lu@intel.com> | 2014-04-01 03:50:27 -0400 |
|---|---|---|
| committer | Zhang Rui <rui.zhang@intel.com> | 2014-10-10 01:57:13 -0400 |
| commit | 9519a6356cbf63b1f22a7a208385dc56092c8b7d (patch) | |
| tree | 6c933347a873e8c3617c26af7710bca27763b5c2 | |
| parent | 19593a1fb1f6718406afca5b867dab184289d406 (diff) | |
ACPI / Fan: add ACPI 4.0 style fan support
This patch adds support for ACPI 4.0 style fan, lacking part is: no
support for 'Low Speed Notification Support', 'Fine Grain Control' is
not used yet.
It's not clear what to do on suspend/resume callback for 4.0 style ACPI
fan, so it does nothing for now.
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
| -rw-r--r-- | drivers/acpi/fan.c | 268 |
1 files changed, 241 insertions, 27 deletions
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 8a5b450576df..f7d1c8027736 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/thermal.h> | 31 | #include <linux/thermal.h> |
| 32 | #include <linux/acpi.h> | 32 | #include <linux/acpi.h> |
| 33 | #include <linux/platform_device.h> | 33 | #include <linux/platform_device.h> |
| 34 | #include <linux/sort.h> | ||
| 34 | 35 | ||
| 35 | MODULE_AUTHOR("Paul Diefenbaugh"); | 36 | MODULE_AUTHOR("Paul Diefenbaugh"); |
| 36 | MODULE_DESCRIPTION("ACPI Fan Driver"); | 37 | MODULE_DESCRIPTION("ACPI Fan Driver"); |
| @@ -59,6 +60,29 @@ static struct dev_pm_ops acpi_fan_pm = { | |||
| 59 | #define FAN_PM_OPS_PTR NULL | 60 | #define FAN_PM_OPS_PTR NULL |
| 60 | #endif | 61 | #endif |
| 61 | 62 | ||
| 63 | struct acpi_fan_fps { | ||
| 64 | u64 control; | ||
| 65 | u64 trip_point; | ||
| 66 | u64 speed; | ||
| 67 | u64 noise_level; | ||
| 68 | u64 power; | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct acpi_fan_fif { | ||
| 72 | u64 revision; | ||
| 73 | u64 fine_grain_ctrl; | ||
| 74 | u64 step_size; | ||
| 75 | u64 low_speed_notification; | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct acpi_fan { | ||
| 79 | bool acpi4; | ||
| 80 | struct acpi_fan_fif fif; | ||
| 81 | struct acpi_fan_fps *fps; | ||
| 82 | int fps_count; | ||
| 83 | struct thermal_cooling_device *cdev; | ||
| 84 | }; | ||
| 85 | |||
| 62 | static struct platform_driver acpi_fan_driver = { | 86 | static struct platform_driver acpi_fan_driver = { |
| 63 | .probe = acpi_fan_probe, | 87 | .probe = acpi_fan_probe, |
| 64 | .remove = acpi_fan_remove, | 88 | .remove = acpi_fan_remove, |
| @@ -73,21 +97,62 @@ static struct platform_driver acpi_fan_driver = { | |||
| 73 | static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long | 97 | static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long |
| 74 | *state) | 98 | *state) |
| 75 | { | 99 | { |
| 76 | /* ACPI fan device only support two states: ON/OFF */ | 100 | struct acpi_device *device = cdev->devdata; |
| 77 | *state = 1; | 101 | struct acpi_fan *fan = acpi_driver_data(device); |
| 102 | |||
| 103 | if (fan->acpi4) | ||
| 104 | *state = fan->fps_count - 1; | ||
| 105 | else | ||
| 106 | *state = 1; | ||
| 78 | return 0; | 107 | return 0; |
| 79 | } | 108 | } |
| 80 | 109 | ||
| 81 | static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long | 110 | static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) |
| 82 | *state) | 111 | { |
| 112 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 113 | struct acpi_fan *fan = acpi_driver_data(device); | ||
| 114 | union acpi_object *obj; | ||
| 115 | acpi_status status; | ||
| 116 | int control, i; | ||
| 117 | |||
| 118 | status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); | ||
| 119 | if (ACPI_FAILURE(status)) { | ||
| 120 | dev_err(&device->dev, "Get fan state failed\n"); | ||
| 121 | return status; | ||
| 122 | } | ||
| 123 | |||
| 124 | obj = buffer.pointer; | ||
| 125 | if (!obj || obj->type != ACPI_TYPE_PACKAGE || | ||
| 126 | obj->package.count != 3 || | ||
| 127 | obj->package.elements[1].type != ACPI_TYPE_INTEGER) { | ||
| 128 | dev_err(&device->dev, "Invalid _FST data\n"); | ||
| 129 | status = -EINVAL; | ||
| 130 | goto err; | ||
| 131 | } | ||
| 132 | |||
| 133 | control = obj->package.elements[1].integer.value; | ||
| 134 | for (i = 0; i < fan->fps_count; i++) { | ||
| 135 | if (control == fan->fps[i].control) | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | if (i == fan->fps_count) { | ||
| 139 | dev_dbg(&device->dev, "Invalid control value returned\n"); | ||
| 140 | status = -EINVAL; | ||
| 141 | goto err; | ||
| 142 | } | ||
| 143 | |||
| 144 | *state = i; | ||
| 145 | |||
| 146 | err: | ||
| 147 | kfree(obj); | ||
| 148 | return status; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int fan_get_state(struct acpi_device *device, unsigned long *state) | ||
| 83 | { | 152 | { |
| 84 | struct acpi_device *device = cdev->devdata; | ||
| 85 | int result; | 153 | int result; |
| 86 | int acpi_state = ACPI_STATE_D0; | 154 | int acpi_state = ACPI_STATE_D0; |
| 87 | 155 | ||
| 88 | if (!device) | ||
| 89 | return -EINVAL; | ||
| 90 | |||
| 91 | result = acpi_device_update_power(device, &acpi_state); | 156 | result = acpi_device_update_power(device, &acpi_state); |
| 92 | if (result) | 157 | if (result) |
| 93 | return result; | 158 | return result; |
| @@ -97,21 +162,57 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long | |||
| 97 | return 0; | 162 | return 0; |
| 98 | } | 163 | } |
| 99 | 164 | ||
| 100 | static int | 165 | static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long |
| 101 | fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) | 166 | *state) |
| 102 | { | 167 | { |
| 103 | struct acpi_device *device = cdev->devdata; | 168 | struct acpi_device *device = cdev->devdata; |
| 104 | int result; | 169 | struct acpi_fan *fan = acpi_driver_data(device); |
| 170 | |||
| 171 | if (fan->acpi4) | ||
| 172 | return fan_get_state_acpi4(device, state); | ||
| 173 | else | ||
| 174 | return fan_get_state(device, state); | ||
| 175 | } | ||
| 105 | 176 | ||
| 106 | if (!device || (state != 0 && state != 1)) | 177 | static int fan_set_state(struct acpi_device *device, unsigned long state) |
| 178 | { | ||
| 179 | if (state != 0 && state != 1) | ||
| 107 | return -EINVAL; | 180 | return -EINVAL; |
| 108 | 181 | ||
| 109 | result = acpi_device_set_power(device, | 182 | return acpi_device_set_power(device, |
| 110 | state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); | 183 | state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); |
| 184 | } | ||
| 111 | 185 | ||
| 112 | return result; | 186 | static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) |
| 187 | { | ||
| 188 | struct acpi_fan *fan = acpi_driver_data(device); | ||
| 189 | acpi_status status; | ||
| 190 | |||
| 191 | if (state >= fan->fps_count) | ||
| 192 | return -EINVAL; | ||
| 193 | |||
| 194 | status = acpi_execute_simple_method(device->handle, "_FSL", | ||
| 195 | fan->fps[state].control); | ||
| 196 | if (ACPI_FAILURE(status)) { | ||
| 197 | dev_dbg(&device->dev, "Failed to set state by _FSL\n"); | ||
| 198 | return status; | ||
| 199 | } | ||
| 200 | |||
| 201 | return 0; | ||
| 113 | } | 202 | } |
| 114 | 203 | ||
| 204 | static int | ||
| 205 | fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) | ||
| 206 | { | ||
| 207 | struct acpi_device *device = cdev->devdata; | ||
| 208 | struct acpi_fan *fan = acpi_driver_data(device); | ||
| 209 | |||
| 210 | if (fan->acpi4) | ||
| 211 | return fan_set_state_acpi4(device, state); | ||
| 212 | else | ||
| 213 | return fan_set_state(device, state); | ||
| 214 | } | ||
| 215 | |||
| 115 | static const struct thermal_cooling_device_ops fan_cooling_ops = { | 216 | static const struct thermal_cooling_device_ops fan_cooling_ops = { |
| 116 | .get_max_state = fan_get_max_state, | 217 | .get_max_state = fan_get_max_state, |
| 117 | .get_cur_state = fan_get_cur_state, | 218 | .get_cur_state = fan_get_cur_state, |
| @@ -122,16 +223,125 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = { | |||
| 122 | Driver Interface | 223 | Driver Interface |
| 123 | -------------------------------------------------------------------------- */ | 224 | -------------------------------------------------------------------------- */ |
| 124 | 225 | ||
| 226 | static bool acpi_fan_is_acpi4(struct acpi_device *device) | ||
| 227 | { | ||
| 228 | return acpi_has_method(device->handle, "_FIF") && | ||
| 229 | acpi_has_method(device->handle, "_FPS") && | ||
| 230 | acpi_has_method(device->handle, "_FSL") && | ||
| 231 | acpi_has_method(device->handle, "_FST"); | ||
| 232 | } | ||
| 233 | |||
| 234 | static int acpi_fan_get_fif(struct acpi_device *device) | ||
| 235 | { | ||
