aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Wang <leozwang@google.com>2016-01-13 16:20:35 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2016-01-13 16:20:35 +0000
commite1513cd0d5d72f711b370c1f3867752147ce1c3b (patch)
tree2212e90a29c249aeb0986bf2ac06c4ea0dab379f
parentc0711310c229de00d7eb155377419ca9a33fd11f (diff)
parenta9cbdf4964e0e7dab73da3e9b1ce0a1c408ddafc (diff)
downloadv4.1-e1513cd0d5d72f711b370c1f3867752147ce1c3b.tar.gz
Merge "FROMLIST: power: reset: add reboot mode driver"
-rw-r--r--drivers/power/reset/Kconfig16
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/reboot-mode.c100
-rw-r--r--drivers/power/reset/reboot-mode.h6
-rw-r--r--drivers/power/reset/syscon-reboot-mode.c62
5 files changed, 186 insertions, 0 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 17d93a73c51..76a6251b85a 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -166,5 +166,21 @@ config POWER_RESET_RMOBILE
help
Reboot support for Renesas R-Mobile and SH-Mobile SoCs.
+config REBOOT_MODE
+ tristate
+ depends on OF
+ help
+ This driver will help to pass the system reboot mode
+ to bootloader
+
+config SYSCON_REBOOT_MODE
+ bool "Generic SYSCON regmap reboot mode driver"
+ select REBOOT_MODE
+ help
+ Say y here will enable reboot mode driver. This will
+ get reboot mode arguments and store it in SYSCON mapped
+ register, then the bootloader can read it to take different
+ action according to the mode.
+
endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index dbe06c36874..e9765502f31 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -19,3 +19,5 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
+obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
+obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
new file mode 100644
index 00000000000..92b7b4dcef7
--- /dev/null
+++ b/drivers/power/reset/reboot-mode.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include "reboot-mode.h"
+
+struct mode_info {
+ const char *mode;
+ int magic;
+ struct list_head list;
+};
+
+struct reboot_mode_driver {
+ struct list_head head;
+ int (*write)(int magic);
+ struct notifier_block reboot_notifier;
+};
+
+static int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
+ const char *cmd)
+{
+ const char *normal = "normal";
+ int magic = 0;
+ struct mode_info *info;
+
+ if (!cmd)
+ cmd = normal;
+
+ list_for_each_entry(info, &reboot->head, list) {
+ if (!strcmp(info->mode, cmd)) {
+ magic = info->magic;
+ break;
+ }
+ }
+
+ return magic;
+}
+
+static int reboot_mode_notify(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct reboot_mode_driver *reboot;
+ int magic;
+
+ reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
+ magic = get_reboot_mode_magic(reboot, cmd);
+ if (magic)
+ reboot->write(magic);
+
+ return NOTIFY_DONE;
+}
+
+int reboot_mode_register(struct device *dev, int (*write)(int))
+{
+ struct reboot_mode_driver *reboot;
+ struct mode_info *info;
+ struct device_node *child;
+ int ret;
+
+ reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL);
+ if (!reboot)
+ return -ENOMEM;
+
+ reboot->write = write;
+ INIT_LIST_HEAD(&reboot->head);
+ for_each_child_of_node(dev->of_node, child) {
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->mode = of_get_property(child, "linux,mode", NULL);
+ if (of_property_read_u32(child, "loader,magic", &info->magic)) {
+ dev_err(dev, "reboot mode %s without magic number\n",
+ info->mode);
+ devm_kfree(dev, info);
+ continue;
+ }
+ list_add_tail(&info->list, &reboot->head);
+ }
+ reboot->reboot_notifier.notifier_call = reboot_mode_notify;
+ ret = register_reboot_notifier(&reboot->reboot_notifier);
+ if (ret)
+ dev_err(dev, "can't register reboot notifier\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(reboot_mode_register);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
+MODULE_DESCRIPTION("System reboot mode driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h
new file mode 100644
index 00000000000..76646e7111e
--- /dev/null
+++ b/drivers/power/reset/reboot-mode.h
@@ -0,0 +1,6 @@
+#ifndef __REBOOT_MODE_H__
+#define __REBOOT_MODE_H__
+
+extern int reboot_mode_register(struct device *dev, int (*write)(int));
+
+#endif
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
new file mode 100644
index 00000000000..b9370f3eb74
--- /dev/null
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include "reboot-mode.h"
+
+static struct regmap *map;
+static u32 offset;
+
+static int syscon_reboot_mode_write(int magic)
+{
+ regmap_write(map, offset, magic);
+
+ return 0;
+}
+
+static int syscon_reboot_mode_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ if (of_property_read_u32(pdev->dev.of_node, "offset", &offset))
+ return -EINVAL;
+ ret = reboot_mode_register(&pdev->dev, syscon_reboot_mode_write);
+ if (ret)
+ dev_err(&pdev->dev, "can't register reboot mode\n");
+
+ return ret;
+}
+
+static const struct of_device_id syscon_reboot_mode_of_match[] = {
+ { .compatible = "syscon-reboot-mode" },
+ {}
+};
+
+static struct platform_driver syscon_reboot_mode_driver = {
+ .probe = syscon_reboot_mode_probe,
+ .driver = {
+ .name = "syscon-reboot-mode",
+ .of_match_table = syscon_reboot_mode_of_match,
+ },
+};
+module_platform_driver(syscon_reboot_mode_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
+MODULE_DESCRIPTION("SYSCON reboot mode driver");
+MODULE_LICENSE("GPL v2");