diff mbox

[1/2] firmware: Introduce reboot_reason driver

Message ID 1450218361-9144-1-git-send-email-john.stultz@linaro.org
State New
Headers show

Commit Message

John Stultz Dec. 15, 2015, 10:26 p.m. UTC
This patch adds a basic driver to allow for commands like
"reboot bootloader" and "reboot recovery" to communicate this
reboot-reason to the bootloader.

This is commonly done on Android devices, in order to reboot
the device into fastboot or recovery mode. It also supports
custom OEM specific commands, via "reboot oem-<value>".

This driver pulls the phys memory address from DT as well as
the magic reason values that are written to the address for
each mode.

Cc: Vishal Bhoj <vishal.bhoj@linaro.org>
Cc: haojian.zhuang@linaro.org
Cc: guodong.xu@linaro.org
Signed-off-by: John Stultz <john.stultz@linaro.org>

---
 drivers/firmware/Kconfig              |   9 +++
 drivers/firmware/Makefile             |   1 +
 drivers/firmware/reboot_reason_sram.c | 107 ++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+)
 create mode 100644 drivers/firmware/reboot_reason_sram.c

-- 
1.9.1
diff mbox

Patch

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6517132..de507b6 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -136,6 +136,15 @@  config QCOM_SCM
 	bool
 	depends on ARM || ARM64
 
+config REBOOT_REASON_SRAM
+	bool "Pass reboot reason to bootloader via SRAM"
+	default n
+	help
+	  On many systems there is a desire to provide a reboot reason to
+	  the bootloader, so that the bootloader can boot into a desired
+	  mode on the next boot. This option enables support for communicating
+	  this reason to the bootloader via SRAM
+
 source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
 
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 3fdd391..5e538bc 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -13,6 +13,7 @@  obj-$(CONFIG_ISCSI_IBFT)	+= iscsi_ibft.o
 obj-$(CONFIG_FIRMWARE_MEMMAP)	+= memmap.o
 obj-$(CONFIG_QCOM_SCM)		+= qcom_scm.o
 CFLAGS_qcom_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+obj-$(CONFIG_REBOOT_REASON_SRAM)+= reboot_reason_sram.o
 
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
 obj-$(CONFIG_EFI)		+= efi/
diff --git a/drivers/firmware/reboot_reason_sram.c b/drivers/firmware/reboot_reason_sram.c
new file mode 100644
index 0000000..af87b6d
--- /dev/null
+++ b/drivers/firmware/reboot_reason_sram.c
@@ -0,0 +1,107 @@ 
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/reboot.h>
+
+/* Types of reasons */
+enum {
+	NONE,
+	BOOTLOADER,
+	RECOVERY,
+	OEM,
+	MAX_REASONS
+};
+
+static u32			reasons[MAX_REASONS];
+static void __iomem		*reboot_reason_val_addr;
+static struct notifier_block	reboot_nb;
+
+static int reboot_reason(struct notifier_block *nb, unsigned long action,
+								void *data)
+{
+	char *cmd = (char *)data;
+	u32 reason = reasons[NONE];
+
+	if (!reboot_reason_val_addr)
+		return NOTIFY_DONE;
+
+	if (cmd != NULL) {
+		if (!strncmp(cmd, "bootloader", 10))
+			reason = reasons[BOOTLOADER];
+		else if (!strncmp(cmd, "recovery", 8))
+			reason = reasons[RECOVERY];
+		else if (!strncmp(cmd, "oem-", 4)) {
+			unsigned long code;
+
+			if (!kstrtoul(cmd+4, 0, &code))
+				reason = reasons[OEM] | (code & 0xff);
+		}
+	}
+
+	if (reason != -1)
+		writel(reason, reboot_reason_val_addr);
+	return NOTIFY_DONE;
+}
+
+static int reboot_reason_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	u32 val;
+	int i;
+
+	/* initialize the reasons */
+	for (i = 0; i < MAX_REASONS; i++)
+		reasons[i] = -1;
+
+	/* Try to grab the reason io address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reboot_reason_val_addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reboot_reason_val_addr))
+		return PTR_ERR(reboot_reason_val_addr);
+
+	/* initialize specified reasons from DT */
+	if (!of_property_read_u32(pdev->dev.of_node, "reason,none", &val))
+		reasons[NONE] = val;
+	if (!of_property_read_u32(pdev->dev.of_node, "reason,bootloader", &val))
+		reasons[BOOTLOADER] = val;
+	if (!of_property_read_u32(pdev->dev.of_node, "reason,recovery", &val))
+		reasons[RECOVERY] = val;
+	if (!of_property_read_u32(pdev->dev.of_node, "reason,oem", &val))
+		reasons[OEM] = val;
+
+	/* Install the notifier */
+	reboot_nb.notifier_call = reboot_reason;
+	reboot_nb.priority = 256;
+	if (register_reboot_notifier(&reboot_nb)) {
+		dev_err(&pdev->dev,
+			"failed to setup restart handler.\n");
+	}
+	return 0;
+}
+
+int reboot_reason_remove(struct platform_device *pdev)
+{
+	unregister_reboot_notifier(&reboot_nb);
+	return 0;
+}
+
+static const struct of_device_id reboot_reason_of_match[] = {
+	{ .compatible = "linux,reboot-reason-sram", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, reboot_reason_of_match);
+
+static struct platform_driver reboot_reason_driver = {
+	.driver = {
+		.name = "reboot-reason-sram",
+		.of_match_table = reboot_reason_of_match,
+	},
+	.probe = reboot_reason_probe,
+	.remove = reboot_reason_remove,
+};
+module_platform_driver(reboot_reason_driver);