]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 24 Oct 2014 18:21:43 +0000 (11:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 24 Oct 2014 18:21:43 +0000 (11:21 -0700)
Pull thermal management updates from Zhang Rui:
 "Sorry that I missed the merge window as there is a bug found in the
  last minute, and I have to fix it and wait for the code to be tested
  in linux-next tree for a few days.  Now the buggy patch has been
  dropped entirely from my next branch.  Thus I hope those changes can
  still be merged in 3.18-rc2 as most of them are platform thermal
  driver changes.

  Specifics:

   - introduce ACPI INT340X thermal drivers.

     Newer laptops and tablets may have thermal sensors and other
     devices with thermal control capabilities that are exposed for the
     OS to use via the ACPI INT340x device objects.  Several drivers are
     introduced to expose the temperature information and cooling
     ability from these objects to user-space via the normal thermal
     framework.

     From: Lu Aaron, Lan Tianyu, Jacob Pan and Zhang Rui.

   - introduce a new thermal governor, which just uses a hysteresis to
     switch abruptly on/off a cooling device.  This governor can be used
     to control certain fan devices that can not be throttled but just
     switched on or off.  From: Peter Feuerer.

   - introduce support for some new thermal interrupt functions on
     i.MX6SX, in IMX thermal driver.  From: Anson, Huang.

   - introduce tracing support on thermal framework.  From: Punit
     Agrawal.

   - small fixes in OF thermal and thermal step_wise governor"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (25 commits)
  Thermal: int340x thermal: select ACPI fan driver
  Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs
  Thermal: int340x_thermal: expose acpi thermal relationship tables
  Thermal: introduce int3403 thermal driver
  Thermal: introduce INT3402 thermal driver
  Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h
  ACPI / Fan: support INT3404 thermal device
  ACPI / Fan: add ACPI 4.0 style fan support
  ACPI / fan: convert to platform driver
  ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant
  ACPI / fan: remove no need check for device pointer
  ACPI / fan: remove unused macro
  Thermal: int3400 thermal: register to thermal framework
  Thermal: int3400 thermal: add capability to detect supporting UUIDs
  Thermal: introduce int3400 thermal driver
  ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package()
  ACPI: make acpi_create_platform_device() an external API
  thermal: step_wise: fix: Prevent from binary overflow when trend is dropping
  ACPI: introduce ACPI int340x thermal scan handler
  thermal: Added Bang-bang thermal governor
  ...

1  2 
drivers/acpi/device_pm.c
drivers/acpi/fan.c
drivers/acpi/scan.c
drivers/acpi/utils.c
drivers/thermal/Kconfig
drivers/thermal/thermal_core.c
include/acpi/acpi_bus.h
include/linux/acpi.h

diff --combined drivers/acpi/device_pm.c
index bea6896be1229b12d5983bb7a738db2c256fd3d1,91775475e3678ecbd316dc1c991ef22dda55450f..7103de034c9e4adcbcd41f55412b0392ee154a82
@@@ -343,6 -343,7 +343,7 @@@ int acpi_device_update_power(struct acp
  
        return 0;
  }
+ EXPORT_SYMBOL_GPL(acpi_device_update_power);
  
  int acpi_bus_update_power(acpi_handle handle, int *state_p)
  {
@@@ -1040,40 -1041,6 +1041,40 @@@ static struct dev_pm_domain acpi_genera
        },
  };
  
 +/**
 + * acpi_dev_pm_detach - Remove ACPI power management from the device.
 + * @dev: Device to take care of.
 + * @power_off: Whether or not to try to remove power from the device.
 + *
 + * Remove the device from the general ACPI PM domain and remove its wakeup
 + * notifier.  If @power_off is set, additionally remove power from the device if
 + * possible.
 + *
 + * Callers must ensure proper synchronization of this function with power
 + * management callbacks.
 + */
 +static void acpi_dev_pm_detach(struct device *dev, bool power_off)
 +{
 +      struct acpi_device *adev = ACPI_COMPANION(dev);
 +
 +      if (adev && dev->pm_domain == &acpi_general_pm_domain) {
 +              dev->pm_domain = NULL;
 +              acpi_remove_pm_notifier(adev);
 +              if (power_off) {
 +                      /*
 +                       * If the device's PM QoS resume latency limit or flags
 +                       * have been exposed to user space, they have to be
 +                       * hidden at this point, so that they don't affect the
 +                       * choice of the low-power state to put the device into.
 +                       */
 +                      dev_pm_qos_hide_latency_limit(dev);
 +                      dev_pm_qos_hide_flags(dev);
 +                      acpi_device_wakeup(adev, ACPI_STATE_S0, false);
 +                      acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
 +              }
 +      }
 +}
 +
  /**
   * acpi_dev_pm_attach - Prepare device for ACPI power management.
   * @dev: Device to prepare.
@@@ -1106,9 -1073,42 +1107,9 @@@ int acpi_dev_pm_attach(struct device *d
                acpi_dev_pm_full_power(adev);
                acpi_device_wakeup(adev, ACPI_STATE_S0, false);
        }
 +
 +      dev->pm_domain->detach = acpi_dev_pm_detach;
        return 0;
  }
  EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
 -
 -/**
 - * acpi_dev_pm_detach - Remove ACPI power management from the device.
 - * @dev: Device to take care of.
 - * @power_off: Whether or not to try to remove power from the device.
 - *
 - * Remove the device from the general ACPI PM domain and remove its wakeup
 - * notifier.  If @power_off is set, additionally remove power from the device if
 - * possible.
 - *
 - * Callers must ensure proper synchronization of this function with power
 - * management callbacks.
 - */
 -void acpi_dev_pm_detach(struct device *dev, bool power_off)
 -{
 -      struct acpi_device *adev = ACPI_COMPANION(dev);
 -
 -      if (adev && dev->pm_domain == &acpi_general_pm_domain) {
 -              dev->pm_domain = NULL;
 -              acpi_remove_pm_notifier(adev);
 -              if (power_off) {
 -                      /*
 -                       * If the device's PM QoS resume latency limit or flags
 -                       * have been exposed to user space, they have to be
 -                       * hidden at this point, so that they don't affect the
 -                       * choice of the low-power state to put the device into.
 -                       */
 -                      dev_pm_qos_hide_latency_limit(dev);
 -                      dev_pm_qos_hide_flags(dev);
 -                      acpi_device_wakeup(adev, ACPI_STATE_S0, false);
 -                      acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
 -              }
 -      }
 -}
 -EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
  #endif /* CONFIG_PM */
diff --combined drivers/acpi/fan.c
index 5328b1090e08681dc4905585470fcd43b2b514f1,e007c4987beaa047833f8a71988f3dd7263de813..caf9b76b7ef83f08bca8d7ba1aac487224d68143
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/types.h>
 -#include <asm/uaccess.h>
 +#include <linux/uaccess.h>
  #include <linux/thermal.h>
  #include <linux/acpi.h>
- #define ACPI_FAN_CLASS                        "fan"
- #define ACPI_FAN_FILE_STATE           "state"
- #define _COMPONENT            ACPI_FAN_COMPONENT
- ACPI_MODULE_NAME("fan");
+ #include <linux/platform_device.h>
+ #include <linux/sort.h>
  
  MODULE_AUTHOR("Paul Diefenbaugh");
  MODULE_DESCRIPTION("ACPI Fan Driver");
  MODULE_LICENSE("GPL");
  
- static int acpi_fan_add(struct acpi_device *device);
- static int acpi_fan_remove(struct acpi_device *device);
+ static int acpi_fan_probe(struct platform_device *pdev);
+ static int acpi_fan_remove(struct platform_device *pdev);
  
  static const struct acpi_device_id fan_device_ids[] = {
        {"PNP0C0B", 0},
+       {"INT3404", 0},
        {"", 0},
  };
  MODULE_DEVICE_TABLE(acpi, fan_device_ids);
@@@ -64,37 -61,100 +61,100 @@@ static struct dev_pm_ops acpi_fan_pm = 
  #define FAN_PM_OPS_PTR NULL
  #endif
  
- static struct acpi_driver acpi_fan_driver = {
-       .name = "fan",
-       .class = ACPI_FAN_CLASS,
-       .ids = fan_device_ids,
-       .ops = {
-               .add = acpi_fan_add,
-               .remove = acpi_fan_remove,
-               },
-       .drv.pm = FAN_PM_OPS_PTR,
+ struct acpi_fan_fps {
+       u64 control;
+       u64 trip_point;
+       u64 speed;
+       u64 noise_level;
+       u64 power;
+ };
+ struct acpi_fan_fif {
+       u64 revision;
+       u64 fine_grain_ctrl;
+       u64 step_size;
+       u64 low_speed_notification;
+ };
+ struct acpi_fan {
+       bool acpi4;
+       struct acpi_fan_fif fif;
+       struct acpi_fan_fps *fps;
+       int fps_count;
+       struct thermal_cooling_device *cdev;
+ };
+ static struct platform_driver acpi_fan_driver = {
+       .probe = acpi_fan_probe,
+       .remove = acpi_fan_remove,
+       .driver = {
+               .name = "acpi-fan",
+               .acpi_match_table = fan_device_ids,
+               .pm = FAN_PM_OPS_PTR,
+       },
  };
  
  /* thermal cooling device callbacks */
  static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
                             *state)
  {
-       /* ACPI fan device only support two states: ON/OFF */
-       *state = 1;
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_fan *fan = acpi_driver_data(device);
+       if (fan->acpi4)
+               *state = fan->fps_count - 1;
+       else
+               *state = 1;
        return 0;
  }
  
- static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
-                            *state)
+ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
+ {
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct acpi_fan *fan = acpi_driver_data(device);
+       union acpi_object *obj;
+       acpi_status status;
+       int control, i;
+       status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
+       if (ACPI_FAILURE(status)) {
+               dev_err(&device->dev, "Get fan state failed\n");
+               return status;
+       }
+       obj = buffer.pointer;
+       if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
+           obj->package.count != 3 ||
+           obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
+               dev_err(&device->dev, "Invalid _FST data\n");
+               status = -EINVAL;
+               goto err;
+       }
+       control = obj->package.elements[1].integer.value;
+       for (i = 0; i < fan->fps_count; i++) {
+               if (control == fan->fps[i].control)
+                       break;
+       }
+       if (i == fan->fps_count) {
+               dev_dbg(&device->dev, "Invalid control value returned\n");
+               status = -EINVAL;
+               goto err;
+       }
+       *state = i;
+ err:
+       kfree(obj);
+       return status;
+ }
+ static int fan_get_state(struct acpi_device *device, unsigned long *state)
  {
-       struct acpi_device *device = cdev->devdata;
        int result;
        int acpi_state = ACPI_STATE_D0;
  
-       if (!device)
-               return -EINVAL;
-       result = acpi_bus_update_power(device->handle, &acpi_state);
+       result = acpi_device_update_power(device, &acpi_state);
        if (result)
                return result;
  
        return 0;
  }
  
- static int
fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
                           *state)
  {
        struct acpi_device *device = cdev->devdata;
-       int result;
+       struct acpi_fan *fan = acpi_driver_data(device);
  
-       if (!device || (state != 0 && state != 1))
+       if (fan->acpi4)
+               return fan_get_state_acpi4(device, state);
+       else
+               return fan_get_state(device, state);
+ }
+ static int fan_set_state(struct acpi_device *device, unsigned long state)
+ {
+       if (state != 0 && state != 1)
                return -EINVAL;
  
-       result = acpi_bus_set_power(device->handle,
-                               state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
+       return acpi_device_set_power(device,
+                                    state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
+ }
  
-       return result;
+ static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
+ {
+       struct acpi_fan *fan = acpi_driver_data(device);
+       acpi_status status;
+       if (state >= fan->fps_count)
+               return -EINVAL;
+       status = acpi_execute_simple_method(device->handle, "_FSL",
+                                           fan->fps[state].control);
+       if (ACPI_FAILURE(status)) {
+               dev_dbg(&device->dev, "Failed to set state by _FSL\n");
+               return status;
+       }
+       return 0;
  }
  
+ static int
+ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+ {
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_fan *fan = acpi_driver_data(device);
+       if (fan->acpi4)
+               return fan_set_state_acpi4(device, state);
+       else
+               return fan_set_state(device, state);
+  }
  static const struct thermal_cooling_device_ops fan_cooling_ops = {
        .get_max_state = fan_get_max_state,
        .get_cur_state = fan_get_cur_state,
  };
  
  /* --------------------------------------------------------------------------
 -                                 Driver Interface
 -   -------------------------------------------------------------------------- */
 + *                               Driver Interface
 + * --------------------------------------------------------------------------
 +*/
  
- static int acpi_fan_add(struct acpi_device *device)
+ static bool acpi_fan_is_acpi4(struct acpi_device *device)
  {
-       int result = 0;
-       struct thermal_cooling_device *cdev;
+       return acpi_has_method(device->handle, "_FIF") &&
+              acpi_has_method(device->handle, "_FPS") &&
+              acpi_has_method(device->handle, "_FSL") &&
+              acpi_has_method(device->handle, "_FST");
+ }
  
-       if (!device)
-               return -EINVAL;
+ static int acpi_fan_get_fif(struct acpi_device *device)
+ {
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct acpi_fan *fan = acpi_driver_data(device);
+       struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
+       struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif };
+       union acpi_object *obj;
+       acpi_status status;
+       status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return status;
+       obj = buffer.pointer;
+       if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
+               dev_err(&device->dev, "Invalid _FIF data\n");
+               status = -EINVAL;
+               goto err;
+       }
  
-       strcpy(acpi_device_name(device), "Fan");
-       strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
+       status = acpi_extract_package(obj, &format, &fif);
+       if (ACPI_FAILURE(status)) {
+               dev_err(&device->dev, "Invalid _FIF element\n");
+               status = -EINVAL;
+       }
  
-       result = acpi_bus_update_power(device->handle, NULL);
-       if (result) {
-               dev_err(&device->dev, "Setting initial power state\n");
-               goto end;
+ err:
+       kfree(obj);
+       return status;
+ }
+ static int acpi_fan_speed_cmp(const void *a, const void *b)
+ {
+       const struct acpi_fan_fps *fps1 = a;
+       const struct acpi_fan_fps *fps2 = b;
+       return fps1->speed - fps2->speed;
+ }
+ static int acpi_fan_get_fps(struct acpi_device *device)
+ {
+       struct acpi_fan *fan = acpi_driver_data(device);
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       acpi_status status;
+       int i;
+       status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return status;
+       obj = buffer.pointer;
+       if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
+               dev_err(&device->dev, "Invalid _FPS data\n");
+               status = -EINVAL;
+               goto err;
+       }
+       fan->fps_count = obj->package.count - 1; /* minus revision field */
+       fan->fps = devm_kzalloc(&device->dev,
+                               fan->fps_count * sizeof(struct acpi_fan_fps),
+                               GFP_KERNEL);
+       if (!fan->fps) {
+               dev_err(&device->dev, "Not enough memory\n");
+               status = -ENOMEM;
+               goto err;
+       }
+       for (i = 0; i < fan->fps_count; i++) {
+               struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+               struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] };
+               status = acpi_extract_package(&obj->package.elements[i + 1],
+                                             &format, &fps);
+               if (ACPI_FAILURE(status)) {
+                       dev_err(&device->dev, "Invalid _FPS element\n");
+                       break;
+               }
+       }
+       /* sort the state array according to fan speed in increase order */
+       sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
+            acpi_fan_speed_cmp, NULL);
+ err:
+       kfree(obj);
+       return status;
+ }
+ static int acpi_fan_probe(struct platform_device *pdev)
+ {
+       int result = 0;
+       struct thermal_cooling_device *cdev;
+       struct acpi_fan *fan;
+       struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
+       fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
+       if (!fan) {
+               dev_err(&device->dev, "No memory for fan\n");
+               return -ENOMEM;
+       }
+       device->driver_data = fan;
+       platform_set_drvdata(pdev, fan);
+       if (acpi_fan_is_acpi4(device)) {
+               if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device))
+                       goto end;
+               fan->acpi4 = true;
+       } else {
+               result = acpi_device_update_power(device, NULL);
+               if (result) {
+                       dev_err(&device->dev, "Setting initial power state\n");
+                       goto end;
+               }
        }
  
        cdev = thermal_cooling_device_register("Fan", device,
                goto end;
        }
  
-       dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
+       dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
  
-       device->driver_data = cdev;
-       result = sysfs_create_link(&device->dev.kobj,
+       fan->cdev = cdev;
+       result = sysfs_create_link(&pdev->dev.kobj,
                                   &cdev->device.kobj,
                                   "thermal_cooling");
        if (result)
-               dev_err(&device->dev, "Failed to create sysfs link "
 -              dev_err(&pdev->dev, "Failed to create sysfs link "
--                      "'thermal_cooling'\n");
++              dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
  
        result = sysfs_create_link(&cdev->device.kobj,
-                                  &device->dev.kobj,
+                                  &pdev->dev.kobj,
                                   "device");
        if (result)
-               dev_err(&device->dev, "Failed to create sysfs link 'device'\n");
-       dev_info(&device->dev, "ACPI: %s [%s] (%s)\n",
-              acpi_device_name(device), acpi_device_bid(device),
-              !device->power.state ? "on" : "off");
 -              dev_err(&pdev->dev, "Failed to create sysfs link "
 -                      "'device'\n");
++              dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
  
  end:
        return result;
  }
  
- static int acpi_fan_remove(struct acpi_device *device)
+ static int acpi_fan_remove(struct platform_device *pdev)
  {
-       struct thermal_cooling_device *cdev;
-       if (!device)
-               return -EINVAL;
-       cdev =  acpi_driver_data(device);
-       if (!cdev)
-               return -EINVAL;
+       struct acpi_fan *fan = platform_get_drvdata(pdev);
  
-       sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
-       sysfs_remove_link(&cdev->device.kobj, "device");
-       thermal_cooling_device_unregister(cdev);
+       sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
+       sysfs_remove_link(&fan->cdev->device.kobj, "device");
+       thermal_cooling_device_unregister(fan->cdev);
  
        return 0;
  }
  #ifdef CONFIG_PM_SLEEP
  static int acpi_fan_suspend(struct device *dev)
  {
-       if (!dev)
-               return -EINVAL;
+       struct acpi_fan *fan = dev_get_drvdata(dev);
+       if (fan->acpi4)
+               return 0;
  
-       acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0);
+       acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
  
        return AE_OK;
  }
  static int acpi_fan_resume(struct device *dev)
  {
        int result;
+       struct acpi_fan *fan = dev_get_drvdata(dev);
  
-       if (!dev)
-               return -EINVAL;
+       if (fan->acpi4)
+               return 0;
  
-       result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
+       result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
        if (result)
                dev_err(dev, "Error updating fan power state\n");
  
  }
  #endif
  
- module_acpi_driver(acpi_fan_driver);
+ module_platform_driver(acpi_fan_driver);
diff --combined drivers/acpi/scan.c
index ae44d8654c8248c73870727098666e2b42dec6f8,eed9740651f813e13ba2041635fda1dd40458b26..095c6ddde8a3a27431de6ac6e3bc7d67fb7369fc
@@@ -130,7 -130,7 +130,7 @@@ static int create_modalias(struct acpi_
        list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
                count = snprintf(&modalias[len], size, "%s:", id->id);
                if (count < 0)
 -                      return EINVAL;
 +                      return -EINVAL;
                if (count >= size)
                        return -ENOMEM;
                len += count;
@@@ -667,14 -667,8 +667,14 @@@ static ssize_
  acpi_device_sun_show(struct device *dev, struct device_attribute *attr,
                     char *buf) {
        struct acpi_device *acpi_dev = to_acpi_device(dev);
 +      acpi_status status;
 +      unsigned long long sun;
 +
 +      status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun);
 +      if (ACPI_FAILURE(status))
 +              return -ENODEV;
  
 -      return sprintf(buf, "%lu\n", acpi_dev->pnp.sun);
 +      return sprintf(buf, "%llu\n", sun);
  }
  static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL);
  
@@@ -696,6 -690,7 +696,6 @@@ static int acpi_device_setup_files(stru
  {
        struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
        acpi_status status;
 -      unsigned long long sun;
        int result = 0;
  
        /*
        if (dev->pnp.unique_id)
                result = device_create_file(&dev->dev, &dev_attr_uid);
  
 -      status = acpi_evaluate_integer(dev->handle, "_SUN", NULL, &sun);
 -      if (ACPI_SUCCESS(status)) {
 -              dev->pnp.sun = (unsigned long)sun;
 +      if (acpi_has_method(dev->handle, "_SUN")) {
                result = device_create_file(&dev->dev, &dev_attr_sun);
                if (result)
                        goto end;
 -      } else {
 -              dev->pnp.sun = (unsigned long)-1;
        }
  
        if (acpi_has_method(dev->handle, "_STA")) {
@@@ -923,17 -922,12 +923,17 @@@ static void acpi_device_notify(acpi_han
        device->driver->ops.notify(device, event);
  }
  
 -static acpi_status acpi_device_notify_fixed(void *data)
 +static void acpi_device_notify_fixed(void *data)
  {
        struct acpi_device *device = data;
  
        /* Fixed hardware devices have no handles */
        acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
 +}
 +
 +static acpi_status acpi_device_fixed_event(void *data)
 +{
 +      acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data);
        return AE_OK;
  }
  
@@@ -944,12 -938,12 +944,12 @@@ static int acpi_device_install_notify_h
        if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
                status =
                    acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
 -                                                   acpi_device_notify_fixed,
 +                                                   acpi_device_fixed_event,
                                                     device);
        else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
                status =
                    acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
 -                                                   acpi_device_notify_fixed,
 +                                                   acpi_device_fixed_event,
                                                     device);
        else
                status = acpi_install_notify_handler(device->handle,
@@@ -966,10 -960,10 +966,10 @@@ static void acpi_device_remove_notify_h
  {
        if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
                acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
 -                                              acpi_device_notify_fixed);
 +                                              acpi_device_fixed_event);
        else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
                acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
 -                                              acpi_device_notify_fixed);
 +                                              acpi_device_fixed_event);
        else
                acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
                                           acpi_device_notify);
@@@ -981,7 -975,7 +981,7 @@@ static int acpi_device_probe(struct dev
        struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
        int ret;
  
 -      if (acpi_dev->handler)
 +      if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev))
                return -EINVAL;
  
        if (!acpi_drv->ops.add)
@@@ -2189,9 -2183,6 +2189,9 @@@ static void acpi_bus_attach(struct acpi
   ok:
        list_for_each_entry(child, &device->children, node)
                acpi_bus_attach(child);
 +
 +      if (device->handler && device->handler->hotplug.notify_online)
 +              device->handler->hotplug.notify_online(device);
  }
  
  /**
@@@ -2315,6 -2306,7 +2315,7 @@@ int __init acpi_scan_init(void
        acpi_container_init();
        acpi_memory_hotplug_init();
        acpi_pnp_init();
+       acpi_int340x_thermal_init();
  
        mutex_lock(&acpi_scan_lock);
        /*
diff --combined drivers/acpi/utils.c
index 834f35c4bf8d50061e1cae9f0b581cdad9467369,1ed7aba99d8dfce7136f22619054a9f1ee5a5bd4..371ac12d25b16ee4c651649a147c7e3e9f9fdc87
@@@ -149,6 -149,21 +149,21 @@@ acpi_extract_package(union acpi_object 
                                break;
                        }
                        break;
+               case ACPI_TYPE_LOCAL_REFERENCE:
+                       switch (format_string[i]) {
+                       case 'R':
+                               size_required += sizeof(void *);
+                               tail_offset += sizeof(void *);
+                               break;
+                       default:
+                               printk(KERN_WARNING PREFIX "Invalid package element"
+                                             " [%d] got reference,"
+                                             " expecting [%c]\n",
+                                             i, format_string[i]);
+                               return AE_BAD_DATA;
+                               break;
+                       }
+                       break;
  
                case ACPI_TYPE_PACKAGE:
                default:
                                break;
                        }
                        break;
+               case ACPI_TYPE_LOCAL_REFERENCE:
+                       switch (format_string[i]) {
+                       case 'R':
+                               *(void **)head =
+                                   (void *)element->reference.handle;
+                               head += sizeof(void *);
+                               break;
+                       default:
+                               /* Should never get here */
+                               break;
+                       }
+                       break;
                case ACPI_TYPE_PACKAGE:
                        /* TBD: handle nested packages... */
                default:
@@@ -661,6 -687,7 +687,6 @@@ EXPORT_SYMBOL(acpi_evaluate_dsm)
   * @uuid: UUID of requested functions, should be 16 bytes at least
   * @rev: revision number of requested functions
   * @funcs: bitmap of requested functions
 - * @exclude: excluding special value, used to support i915 and nouveau
   *
   * Evaluate device's _DSM method to check whether it supports requested
   * functions. Currently only support 64 functions at maximum, should be
diff --combined drivers/thermal/Kconfig
index ef5587fe2c69d05fdf1934eb93a80788e4b87cf5,9b012ff652200ac9b03d6648f1604c07ffe528cc..f554d25b439971f126b55c6ec3ed38271ab502aa
@@@ -84,6 -84,16 +84,16 @@@ config THERMAL_GOV_STEP_WIS
          Enable this to manage platform thermals using a simple linear
          governor.
  
+ config THERMAL_GOV_BANG_BANG
+       bool "Bang Bang thermal governor"
+       default n
+       help
+         Enable this to manage platform thermals using bang bang governor.
+         Say 'Y' here if you want to use two point temperature regulation
+         used for fans without throttling.  Some fan drivers depend on this
+         governor to be enabled (e.g. acerhdf).
  config THERMAL_GOV_USER_SPACE
        bool "User_space thermal governor"
        help
@@@ -143,7 -153,7 +153,7 @@@ config RCAR_THERMA
  
  config KIRKWOOD_THERMAL
        tristate "Temperature sensor on Marvell Kirkwood SoCs"
 -      depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
 +      depends on MACH_KIRKWOOD
        depends on OF
        help
          Support for the Kirkwood thermal sensor driver into the Linux thermal
@@@ -207,21 -217,6 +217,6 @@@ config X86_PKG_TEMP_THERMA
          two trip points which can be set by user to get notifications via thermal
          notification methods.
  
- config ACPI_INT3403_THERMAL
-       tristate "ACPI INT3403 thermal driver"
-       depends on X86 && ACPI
-       help
-         Newer laptops and tablets that use ACPI may have thermal sensors
-         outside the core CPU/SOC for thermal safety reasons. These
-         temperature sensors are also exposed for the OS to use via the so
-         called INT3403 ACPI object. This driver will, on devices that have
-         such sensors, expose the temperature information from these sensors
-         to userspace via the normal thermal framework. This means that a wide
-         range of applications and GUI widgets can show this information to
-         the user or use this information for making decisions. For example,
-         the Intel Thermal Daemon can use this information to allow the user
-         to select his laptop to run without turning on the fans.
  config INTEL_SOC_DTS_THERMAL
        tristate "Intel SoCs DTS thermal driver"
        depends on X86 && IOSF_MBI
          notification methods.The other trip is a critical trip point, which
          was set by the driver based on the TJ MAX temperature.
  
+ config INT340X_THERMAL
+       tristate "ACPI INT340X thermal drivers"
+       depends on X86 && ACPI
+       select THERMAL_GOV_USER_SPACE
+       select ACPI_THERMAL_REL
+       select ACPI_FAN
+       help
+         Newer laptops and tablets that use ACPI may have thermal sensors and
+         other devices with thermal control capabilities outside the core
+         CPU/SOC, for thermal safety reasons.
+         They are exposed for the OS to use via the INT3400 ACPI device object
+         as the master, and INT3401~INT340B ACPI device objects as the slaves.
+         Enable this to expose the temperature information and cooling ability
+         from these objects to userspace via the normal thermal framework.
+         This means that a wide range of applications and GUI widgets can show
+         the information to the user or use this information for making
+         decisions. For example, the Intel Thermal Daemon can use this
+         information to allow the user to select his laptop to run without
+         turning on the fans.
+ config ACPI_THERMAL_REL
+       tristate
+       depends on ACPI
  menu "Texas Instruments thermal drivers"
  source "drivers/thermal/ti-soc-thermal/Kconfig"
  endmenu
index 1e23f4f8d2c2099cc2f634010e4ff8ff2c2db11c,976b8aee1da496a8fa4a1b654f44fccb4a7b7ca9..9bf10aa6069bbed102735da623ebfedb711e5d20
@@@ -38,6 -38,9 +38,9 @@@
  #include <net/netlink.h>
  #include <net/genetlink.h>
  
+ #define CREATE_TRACE_POINTS
+ #include <trace/events/thermal.h>
  #include "thermal_core.h"
  #include "thermal_hwmon.h"
  
@@@ -66,7 -69,7 +69,7 @@@ static struct thermal_governor *__find_
                return def_governor;
  
        list_for_each_entry(pos, &thermal_governor_list, governor_list)
 -              if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH))
 +              if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH))
                        return pos;
  
        return NULL;
@@@ -104,7 -107,7 +107,7 @@@ int thermal_register_governor(struct th
  
                name = pos->tzp->governor_name;
  
 -              if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH))
 +              if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH))
                        pos->governor = governor;
        }
  
@@@ -129,7 -132,7 +132,7 @@@ void thermal_unregister_governor(struc
        mutex_lock(&thermal_list_lock);
  
        list_for_each_entry(pos, &thermal_tz_list, node) {
 -              if (!strnicmp(pos->governor->name, governor->name,
 +              if (!strncasecmp(pos->governor->name, governor->name,
                                                THERMAL_NAME_LENGTH))
                        pos->governor = NULL;
        }
@@@ -368,6 -371,8 +371,8 @@@ static void handle_critical_trips(struc
        if (tz->temperature < trip_temp)
                return;
  
+       trace_thermal_zone_trip(tz, trip, trip_type);
        if (tz->ops->notify)
                tz->ops->notify(tz, trip, trip_type);
  
@@@ -463,6 -468,7 +468,7 @@@ static void update_temperature(struct t
        tz->temperature = temp;
        mutex_unlock(&tz->lock);
  
+       trace_thermal_temperature(tz);
        dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
                                tz->last_temperature, tz->temperature);
  }
@@@ -1287,6 -1293,7 +1293,7 @@@ void thermal_cdev_update(struct thermal
        mutex_unlock(&cdev->lock);
        cdev->ops->set_cur_state(cdev, target);
        cdev->updated = true;
+       trace_cdev_update(cdev, target);
        dev_dbg(&cdev->device, "set to state %lu\n", target);
  }
  EXPORT_SYMBOL(thermal_cdev_update);
@@@ -1665,7 -1672,7 +1672,7 @@@ struct thermal_zone_device *thermal_zon
  
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(pos, &thermal_tz_list, node)
 -              if (!strnicmp(name, pos->type, THERMAL_NAME_LENGTH)) {
 +              if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) {
                        found++;
                        ref = pos;
                }
@@@ -1790,6 -1797,10 +1797,10 @@@ static int __init thermal_register_gove
        if (result)
                return result;
  
+       result = thermal_gov_bang_bang_register();
+       if (result)
+               return result;
        return thermal_gov_user_space_register();
  }
  
@@@ -1797,6 -1808,7 +1808,7 @@@ static void thermal_unregister_governor
  {
        thermal_gov_step_wise_unregister();
        thermal_gov_fair_share_unregister();
+       thermal_gov_bang_bang_unregister();
        thermal_gov_user_space_unregister();
  }
  
diff --combined include/acpi/acpi_bus.h
index 57ee0528aacb16db912a11571c518bd0fc9eb5b5,6ca32812f3da3779565aebecd74fa4dc4f36de7e..f34a0835aa4f230f47c57d67e64425d80ba851b7
@@@ -118,7 -118,6 +118,7 @@@ struct acpi_device
  struct acpi_hotplug_profile {
        struct kobject kobj;
        int (*scan_dependent)(struct acpi_device *adev);
 +      void (*notify_online)(struct acpi_device *adev);
        bool enabled:1;
        bool demand_offline:1;
  };
@@@ -205,9 -204,10 +205,9 @@@ struct acpi_device_flags 
        u32 match_driver:1;
        u32 initialized:1;
        u32 visited:1;
 -      u32 no_hotplug:1;
        u32 hotplug_notify:1;
        u32 is_dock_station:1;
 -      u32 reserved:22;
 +      u32 reserved:23;
  };
  
  /* File System */
@@@ -246,6 -246,7 +246,6 @@@ struct acpi_device_pnp 
        acpi_device_name device_name;   /* Driver-determined */
        acpi_device_class device_class; /*        "          */
        union acpi_object *str_obj;     /* unicode string for _STR method */
 -      unsigned long sun;              /* _SUN */
  };
  
  #define acpi_device_bid(d)    ((d)->pnp.bus_id)
@@@ -411,6 -412,7 +411,6 @@@ void acpi_bus_private_data_handler(acpi
  int acpi_bus_get_private_data(acpi_handle, void **);
  int acpi_bus_attach_private_data(acpi_handle, void *);
  void acpi_bus_detach_private_data(acpi_handle);
 -void acpi_bus_no_hotplug(acpi_handle handle);
  extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32);
  extern int register_acpi_notifier(struct notifier_block *);
  extern int unregister_acpi_notifier(struct notifier_block *);
@@@ -433,6 -435,7 +433,7 @@@ int acpi_device_set_power(struct acpi_d
  int acpi_bus_init_power(struct acpi_device *device);
  int acpi_device_fix_up_power(struct acpi_device *device);
  int acpi_bus_update_power(acpi_handle handle, int *state_p);
+ int acpi_device_update_power(struct acpi_device *device, int *state_p);
  bool acpi_bus_power_manageable(acpi_handle handle);
  
  #ifdef CONFIG_PM
diff --combined include/linux/acpi.h
index b7926bb9b4442f90d2d7f32d8c11a07370bfe8c9,2c24c2c1be458ab6f8638ee1056d334ab8fa3102..407a12f663ebdd313c166dcc4f5c892da1453ecf
@@@ -432,6 -432,7 +432,7 @@@ static inline bool acpi_driver_match_de
  int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
  int acpi_device_modalias(struct device *, char *, int);
  
+ struct platform_device *acpi_create_platform_device(struct acpi_device *);
  #define ACPI_PTR(_ptr)        (_ptr)
  
  #else /* !CONFIG_ACPI */
@@@ -587,6 -588,7 +588,6 @@@ static inline int acpi_subsys_freeze(st
  #if defined(CONFIG_ACPI) && defined(CONFIG_PM)
  struct acpi_device *acpi_dev_pm_get_node(struct device *dev);
  int acpi_dev_pm_attach(struct device *dev, bool power_on);
 -void acpi_dev_pm_detach(struct device *dev, bool power_off);
  #else
  static inline struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
  {
@@@ -596,6 -598,7 +597,6 @@@ static inline int acpi_dev_pm_attach(st
  {
        return -ENODEV;
  }
 -static inline void acpi_dev_pm_detach(struct device *dev, bool power_off) {}
  #endif
  
  #ifdef CONFIG_ACPI