Merge remote-tracking branch 'nouveau/drm-nouveau-next' into drm-fixes
[firefly-linux-kernel-4.4.55.git] / drivers / tty / sysrq.c
index 5f68f2cfdfd0fe7b263521af8e2f237d8088a27d..40a9fe9d3b10f0170af31c4d8845d8cc609e6bb5 100644 (file)
@@ -44,6 +44,8 @@
 #include <linux/uaccess.h>
 #include <linux/moduleparam.h>
 #include <linux/jiffies.h>
+#include <linux/syscalls.h>
+#include <linux/of.h>
 
 #include <asm/ptrace.h>
 #include <asm/irq_regs.h>
@@ -586,6 +588,7 @@ struct sysrq_state {
 
        /* reset sequence handling */
        bool reset_canceled;
+       bool reset_requested;
        unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)];
        int reset_seq_len;
        int reset_seq_cnt;
@@ -624,18 +627,26 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
        state->reset_seq_version = sysrq_reset_seq_version;
 }
 
-static void sysrq_do_reset(unsigned long dummy)
+static void sysrq_do_reset(unsigned long _state)
 {
-       __handle_sysrq(sysrq_xlate[KEY_B], false);
+       struct sysrq_state *state = (struct sysrq_state *) _state;
+
+       state->reset_requested = true;
+
+       sys_sync();
+       kernel_restart(NULL);
 }
 
 static void sysrq_handle_reset_request(struct sysrq_state *state)
 {
+       if (state->reset_requested)
+               __handle_sysrq(sysrq_xlate[KEY_B], false);
+
        if (sysrq_reset_downtime_ms)
                mod_timer(&state->keyreset_timer,
                        jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms));
        else
-               sysrq_do_reset(0);
+               sysrq_do_reset((unsigned long)state);
 }
 
 static void sysrq_detect_reset_sequence(struct sysrq_state *state,
@@ -671,6 +682,40 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state,
        }
 }
 
+#ifdef CONFIG_OF
+static void sysrq_of_get_keyreset_config(void)
+{
+       u32 key;
+       struct device_node *np;
+       struct property *prop;
+       const __be32 *p;
+
+       np = of_find_node_by_path("/chosen/linux,sysrq-reset-seq");
+       if (!np) {
+               pr_debug("No sysrq node found");
+               return;
+       }
+
+       /* Reset in case a __weak definition was present */
+       sysrq_reset_seq_len = 0;
+
+       of_property_for_each_u32(np, "keyset", prop, p, key) {
+               if (key == KEY_RESERVED || key > KEY_MAX ||
+                   sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX)
+                       break;
+
+               sysrq_reset_seq[sysrq_reset_seq_len++] = (unsigned short)key;
+       }
+
+       /* Get reset timeout if any. */
+       of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms);
+}
+#else
+static void sysrq_of_get_keyreset_config(void)
+{
+}
+#endif
+
 static void sysrq_reinject_alt_sysrq(struct work_struct *work)
 {
        struct sysrq_state *sysrq =
@@ -837,7 +882,8 @@ static int sysrq_connect(struct input_handler *handler,
        sysrq->handle.handler = handler;
        sysrq->handle.name = "sysrq";
        sysrq->handle.private = sysrq;
-       setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0);
+       setup_timer(&sysrq->keyreset_timer,
+                   sysrq_do_reset, (unsigned long)sysrq);
 
        error = input_register_handle(&sysrq->handle);
        if (error) {
@@ -903,6 +949,7 @@ static inline void sysrq_register_handler(void)
        int error;
        int i;
 
+       /* First check if a __weak interface was instantiated. */
        for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
                key = platform_sysrq_reset_seq[i];
                if (key == KEY_RESERVED || key > KEY_MAX)
@@ -911,6 +958,12 @@ static inline void sysrq_register_handler(void)
                sysrq_reset_seq[sysrq_reset_seq_len++] = key;
        }
 
+       /*
+        * DT configuration takes precedence over anything that would
+        * have been defined via the __weak interface.
+        */
+       sysrq_of_get_keyreset_config();
+
        error = input_register_handler(&sysrq_handler);
        if (error)
                pr_err("Failed to register input handler, error %d", error);