]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/thermal/thermal_core.c
net: dsa: change scope of bridging code
[karo-tx-linux.git] / drivers / thermal / thermal_core.c
index 11f0675cb7e552456d36ec49c82a46d5cf016d04..b21b9cc2c8d6412897193a4ce53e295966737e9c 100644 (file)
@@ -45,8 +45,10 @@ static LIST_HEAD(thermal_governor_list);
 
 static DEFINE_MUTEX(thermal_list_lock);
 static DEFINE_MUTEX(thermal_governor_lock);
+static DEFINE_MUTEX(poweroff_lock);
 
 static atomic_t in_suspend;
+static bool power_off_triggered;
 
 static struct thermal_governor *def_governor;
 
@@ -322,6 +324,54 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz,
                       def_governor->throttle(tz, trip);
 }
 
+/**
+ * thermal_emergency_poweroff_func - emergency poweroff work after a known delay
+ * @work: work_struct associated with the emergency poweroff function
+ *
+ * This function is called in very critical situations to force
+ * a kernel poweroff after a configurable timeout value.
+ */
+static void thermal_emergency_poweroff_func(struct work_struct *work)
+{
+       /*
+        * We have reached here after the emergency thermal shutdown
+        * Waiting period has expired. This means orderly_poweroff has
+        * not been able to shut off the system for some reason.
+        * Try to shut down the system immediately using kernel_power_off
+        * if populated
+        */
+       WARN(1, "Attempting kernel_power_off: Temperature too high\n");
+       kernel_power_off();
+
+       /*
+        * Worst of the worst case trigger emergency restart
+        */
+       WARN(1, "Attempting emergency_restart: Temperature too high\n");
+       emergency_restart();
+}
+
+static DECLARE_DELAYED_WORK(thermal_emergency_poweroff_work,
+                           thermal_emergency_poweroff_func);
+
+/**
+ * thermal_emergency_poweroff - Trigger an emergency system poweroff
+ *
+ * This may be called from any critical situation to trigger a system shutdown
+ * after a known period of time. By default this is not scheduled.
+ */
+void thermal_emergency_poweroff(void)
+{
+       int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS;
+       /*
+        * poweroff_delay_ms must be a carefully profiled positive value.
+        * Its a must for thermal_emergency_poweroff_work to be scheduled
+        */
+       if (poweroff_delay_ms <= 0)
+               return;
+       schedule_delayed_work(&thermal_emergency_poweroff_work,
+                             msecs_to_jiffies(poweroff_delay_ms));
+}
+
 static void handle_critical_trips(struct thermal_zone_device *tz,
                                  int trip, enum thermal_trip_type trip_type)
 {
@@ -342,7 +392,17 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
                dev_emerg(&tz->device,
                          "critical temperature reached(%d C),shutting down\n",
                          tz->temperature / 1000);
-               orderly_poweroff(true);
+               mutex_lock(&poweroff_lock);
+               if (!power_off_triggered) {
+                       /*
+                        * Queue a backup emergency shutdown in the event of
+                        * orderly_poweroff failure
+                        */
+                       thermal_emergency_poweroff();
+                       orderly_poweroff(true);
+                       power_off_triggered = true;
+               }
+               mutex_unlock(&poweroff_lock);
        }
 }
 
@@ -1463,6 +1523,7 @@ static int __init thermal_init(void)
 {
        int result;
 
+       mutex_init(&poweroff_lock);
        result = thermal_register_governors();
        if (result)
                goto error;
@@ -1497,6 +1558,7 @@ error:
        ida_destroy(&thermal_cdev_ida);
        mutex_destroy(&thermal_list_lock);
        mutex_destroy(&thermal_governor_lock);
+       mutex_destroy(&poweroff_lock);
        return result;
 }