===================================================================
@@ -0,0 +1,79 @@
+/*
+ * Machine reset hooks management
+ *
+ * Copyright (C) 2013 Domenico Andreoli <domenico.andreoli@linux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/reboot.h>
+
+enum reset_func {
+ RESET_RESTART = 0,
+ RESET_HALT,
+ RESET_POWER_OFF_PREPARE,
+ RESET_POWER_OFF,
+ RESET_CRASH_POWER_OFF,
+ RESET_NR_FUNCS,
+};
+
+struct reset_hook {
+ union {
+ void (*restart)(void *dev, enum reboot_mode, const char *cmd);
+ void (*halt)(void *dev);
+ void (*power_off_prepare)(void *dev);
+ void (*power_off)(void *dev);
+ void (*crash_power_off)(void *dev);
+ void (*handler)(void *dev);
+ };
+ void (*release)(void *dev);
+};
+
+static inline
+void reset_hook_init(struct reset_hook *hook)
+{
+ hook->handler = NULL;
+ hook->release = NULL;
+}
+
+#ifdef CONFIG_MACHINE_RESET
+
+void set_machine_reset(enum reset_func, const struct reset_hook *, void *dev);
+void unset_machine_reset(enum reset_func, const struct reset_hook *);
+int isset_machine_reset(enum reset_func, void *dev);
+
+void default_restart(enum reboot_mode, const char *cmd);
+void default_halt(void);
+void default_power_off_prepare(void);
+void default_power_off(void);
+void default_crash_power_off(void);
+
+#else /* CONFIG_MACHINE_RESET */
+
+static inline
+void set_machine_reset(enum reset_func func,
+ const struct reset_hook *hook, void *dev)
+{
+ if (hook->release)
+ hook->release(dev);
+}
+
+static inline void unset_machine_reset(enum reset_func _f,
+ const struct reset_hook *_h) {}
+static inline int isset_machine_reset(enum reset_func _f, void *_d) { return 0; }
+
+static inline void default_restart(enum reboot_mode _m, const char *_c) {}
+static inline void default_halt(void) {}
+static inline void default_power_off_prepare(void) {}
+static inline void default_power_off(void) {}
+static inline void default_crash_power_off(void) {}
+
+#endif /* CONFIG_MACHINE_RESET */
===================================================================
@@ -111,6 +111,7 @@ obj-$(CONFIG_PADATA) += padata.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o
+obj-$(CONFIG_MACHINE_RESET) += machine_reset.o
$(obj)/configs.o: $(obj)/config_data.h
===================================================================
@@ -0,0 +1,131 @@
+/*
+ * Machine reset hooks management
+ *
+ * Copyright (C) 2013 Domenico Andreoli <domenico.andreoli@linux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/spinlock.h>
+#include <linux/reboot.h>
+#include <linux/machine_reset.h>
+
+struct reset_descr {
+ struct reset_hook hook;
+ void *dev;
+};
+static DEFINE_SPINLOCK(descr_lock);
+
+static struct reset_descr *get_descr(enum reset_func func)
+{
+ static struct reset_descr descr[RESET_NR_FUNCS];
+
+ BUG_ON(func >= RESET_NR_FUNCS);
+ spin_lock(&descr_lock);
+ return &descr[func];
+}
+
+static void put_descr(struct reset_descr *descr)
+{
+ spin_unlock(&descr_lock);
+}
+
+#define RESET_FUNC(_func, _member, _args...) \
+{ \
+ struct reset_descr *descr = get_descr(_func); \
+ typeof(descr->hook._member) member = descr->hook._member; \
+ if (member) { \
+ pr_debug("machine_reset: %s (%pf)\n", #_func, member); \
+ member(descr->dev , ##_args); \
+ } \
+ put_descr(descr); \
+ pr_emerg("machine_reset: %s: FAILED\n", #_func); \
+ while (1); \
+}
+
+void default_restart(enum reboot_mode reboot_mode, const char *cmd)
+{
+ RESET_FUNC(RESET_RESTART, restart, reboot_mode, cmd);
+}
+
+void default_halt(void)
+{
+ RESET_FUNC(RESET_HALT, halt);
+}
+
+void default_power_off_prepare(void)
+{
+ RESET_FUNC(RESET_POWER_OFF_PREPARE, power_off_prepare);
+}
+
+void default_power_off(void)
+{
+ RESET_FUNC(RESET_POWER_OFF, power_off);
+}
+
+void default_crash_power_off(void)
+{
+ RESET_FUNC(RESET_CRASH_POWER_OFF, crash_power_off);
+}
+
+void set_machine_reset(enum reset_func func,
+ const struct reset_hook *hook, void *new_dev)
+{
+ struct reset_descr *descr;
+ void (*new_handler)(void *) = hook ? hook->handler : NULL;
+ void (*new_release)(void *) = hook ? hook->release : NULL;
+ void (*old_release)(void *) = NULL;
+ void *old_dev;
+
+ descr = get_descr(func);
+ old_release = descr->hook.release;
+ old_dev = descr->dev;
+ reset_hook_init(&descr->hook);
+ descr->hook.handler = new_handler;
+ descr->hook.release = new_release;
+ descr->dev = new_dev;
+ put_descr(descr);
+
+ if (old_release)
+ old_release(old_dev);
+}
+EXPORT_SYMBOL_GPL(set_machine_reset);
+
+void unset_machine_reset(enum reset_func func, const struct reset_hook *hook)
+{
+ struct reset_descr *descr;
+ void (*old_release)(void *) = NULL;
+ void *old_dev;
+
+ BUG_ON(!hook || !hook->handler);
+
+ descr = get_descr(func);
+ if (descr->hook.handler == hook->handler) {
+ old_release = descr->hook.release;
+ old_dev = descr->dev;
+ reset_hook_init(&descr->hook);
+ }
+ put_descr(descr);
+
+ if (old_release)
+ old_release(old_dev);
+}
+EXPORT_SYMBOL_GPL(unset_machine_reset);
+
+int isset_machine_reset(enum reset_func func, void *dev)
+{
+ struct reset_descr *descr = get_descr(func);
+ int ret = descr->hook.handler && (!dev || descr->dev == dev);
+ put_descr(descr);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(isset_machine_reset);
===================================================================
@@ -293,3 +293,7 @@ config PM_GENERIC_DOMAINS_RUNTIME
config CPU_PM
bool
depends on SUSPEND || CPU_IDLE
+
+config MACHINE_RESET
+ bool
+ default n
===================================================================
@@ -12,6 +12,7 @@
#include <linux/kmod.h>
#include <linux/kmsg_dump.h>
#include <linux/reboot.h>
+#include <linux/machine_reset.h>
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/syscore_ops.h>
@@ -178,6 +179,8 @@ void kernel_power_off(void)
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
+ else
+ default_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("Power down\n");