From 205d6619be42628287532019430e84a7fda1caf1 Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Mon, 9 Dec 2019 09:15:16 -0800 Subject: [PATCH] power: reset: Add qcom reboot reason driver Add support for setting reboot reason on Qualcomm boards. This driver uses reboot notifier chain to support commands such as "reboot bootloader" to boot board to fastboot mode. Change-Id: I3ccaa926dc92df9d0acd4261c79ff7f3c9e1c438 Signed-off-by: Elliot Berman --- drivers/power/reset/Kconfig | 9 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/qcom-reboot-reason.c | 111 +++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/power/reset/qcom-reboot-reason.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 5795c1665f89..8b90e07c1b2a 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -135,6 +135,15 @@ config POWER_RESET_QCOM_PON power-on and reboot reason, Say Y. If unsure, Say N. +config POWER_RESET_QCOM_REBOOT_REASON + tristate "MSM reboot mode driver" + depends on ARCH_QCOM + help + Support for setting reboot reason on MSM boards. + This feature supports commands such as "reboot bootloader" + to boot the MSM board to fastboot mode. + If unsure, say Y. + config POWER_RESET_OCELOT_RESET bool "Microsemi Ocelot reset driver" depends on MSCC_OCELOT || COMPILE_TEST diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 6f0ee7819909..5c68698f8920 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_QCOM_DOWNLOAD_MODE) += qcom-dload-mode.o obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o +obj-$(CONFIG_POWER_RESET_QCOM_REBOOT_REASON) += qcom-reboot-reason.o obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o diff --git a/drivers/power/reset/qcom-reboot-reason.c b/drivers/power/reset/qcom-reboot-reason.c new file mode 100644 index 000000000000..483e974121fc --- /dev/null +++ b/drivers/power/reset/qcom-reboot-reason.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct qcom_reboot_reason { + struct device *dev; + struct notifier_block reboot_nb; + struct nvmem_cell *nvmem_cell; +}; + +struct poweroff_reason { + const char *cmd; + unsigned char pon_reason; +}; + +static struct poweroff_reason reasons[] = { + { "recovery", 0x01 }, + { "bootloader", 0x02 }, + { "rtc", 0x03 }, + { "dm-verity device corrupted", 0x04 }, + { "dm-verity enforcing", 0x05 }, + { "keys clear", 0x06 }, + {} +}; + +static int qcom_reboot_reason_reboot(struct notifier_block *this, + unsigned long event, void *ptr) +{ + char *cmd = ptr; + struct qcom_reboot_reason *reboot = container_of(this, + struct qcom_reboot_reason, reboot_nb); + struct poweroff_reason *reason; + + if (!cmd) + return NOTIFY_OK; + for (reason = reasons; reason->cmd; reason++) { + if (!strcmp(cmd, reason->cmd)) { + nvmem_cell_write(reboot->nvmem_cell, + &reason->pon_reason, + sizeof(reason->pon_reason)); + break; + } + } + + return NOTIFY_OK; +} + +static int qcom_reboot_reason_probe(struct platform_device *pdev) +{ + struct qcom_reboot_reason *reboot; + + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + reboot->dev = &pdev->dev; + + reboot->nvmem_cell = nvmem_cell_get(reboot->dev, "restart_reason"); + + if (IS_ERR(reboot->nvmem_cell)) + return PTR_ERR(reboot->nvmem_cell); + + reboot->reboot_nb.notifier_call = qcom_reboot_reason_reboot; + reboot->reboot_nb.priority = 255; + register_reboot_notifier(&reboot->reboot_nb); + + platform_set_drvdata(pdev, reboot); + + return 0; +} + +static int qcom_reboot_reason_remove(struct platform_device *pdev) +{ + struct qcom_reboot_reason *reboot = platform_get_drvdata(pdev); + + unregister_reboot_notifier(&reboot->reboot_nb); + + return 0; +} + +static const struct of_device_id of_qcom_reboot_reason_match[] = { + { .compatible = "qcom,reboot-reason", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_qcom_reboot_reason_match); + +static struct platform_driver qcom_reboot_reason_driver = { + .probe = qcom_reboot_reason_probe, + .remove = qcom_reboot_reason_remove, + .driver = { + .name = "qcom-reboot-reason", + .of_match_table = of_match_ptr(of_qcom_reboot_reason_match), + }, +}; + +module_platform_driver(qcom_reboot_reason_driver); + +MODULE_DESCRIPTION("MSM Reboot Reason Driver"); +MODULE_LICENSE("GPL v2");