summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJone Chou <jonechou@google.com>2022-07-05 23:44:01 +0800
committerJone Chou <jonechou@google.com>2022-07-06 17:44:38 +0800
commitfbb0cfb74551de7255964e0964a56bf23bcba060 (patch)
tree40fa67801a6350d63a8dcd534cc003a078946b61
parentffb7c17de003c5226344505eae1c49af68d2e32a (diff)
downloadreset-fbb0cfb74551de7255964e0964a56bf23bcba060.tar.gz
power: reset: introduce exynos-reboot module
Initialize zuma reset driver. Bug: 236191150 Signed-off-by: Jone Chou <jonechou@google.com> Change-Id: Ifa3d67d7a692e834b26fcfada67ccb3dd0bb1e95
-rw-r--r--Kbuild1
-rw-r--r--Kconfig2
-rw-r--r--exynos-zuma-reboot.c210
3 files changed, 212 insertions, 1 deletions
diff --git a/Kbuild b/Kbuild
index 3191986..4a1d53d 100644
--- a/Kbuild
+++ b/Kbuild
@@ -1,4 +1,5 @@
exynos-reboot-$(CONFIG_SOC_GS101) += exynos-gs101-reboot.o
exynos-reboot-$(CONFIG_SOC_GS201) += exynos-gs201-reboot.o
+exynos-reboot-$(CONFIG_SOC_ZUMA) += exynos-zuma-reboot.o
obj-$(CONFIG_POWER_RESET_EXYNOS) += exynos-reboot.o
diff --git a/Kconfig b/Kconfig
index 377e513..b7de435 100644
--- a/Kconfig
+++ b/Kconfig
@@ -2,6 +2,6 @@
config POWER_RESET_EXYNOS
tristate "Samsung Exynos power-off driver"
- depends on SOC_GS101 || SOC_GS201
+ depends on SOC_GS101 || SOC_GS201 || SOC_ZUMA
help
Reboot support for Samsung Exynos boards.
diff --git a/exynos-zuma-reboot.c b/exynos-zuma-reboot.c
new file mode 100644
index 0000000..7060be8
--- /dev/null
+++ b/exynos-zuma-reboot.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * exynos-reboot.c - Samsung Exynos SoC reset code
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Author: Hyunki Koo <hyunki00.koo@samsung.com>
+ * Youngmin Nam <youngmin.nam@samsung.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <soc/google/exynos-el3_mon.h>
+/* [TODO] b/238076990: wait for gbms module is ready
+ #include "../../bms/google_bms.h"
+ */
+
+#define EXYNOS_PMU_SYSIP_DAT0 (0x0810)
+
+#define BMS_RSBM_VALID BIT(31)
+
+static u32 reboot_cmd_offset;
+static phys_addr_t pmu_alive_base;
+static bool force_warm_reboot_on_thermal_shutdown;
+
+enum pon_reboot_mode {
+ REBOOT_MODE_NORMAL = 0x00,
+ REBOOT_MODE_CHARGE = 0x0A,
+
+ REBOOT_MODE_DMVERITY_CORRUPTED = 0x50,
+ REBOOT_MODE_SHUTDOWN_THERMAL = 0x51,
+ REBOOT_MODE_AB_UPDATE = 0x52,
+
+ REBOOT_MODE_RESCUE = 0xF9,
+ REBOOT_MODE_FASTBOOT = 0xFA,
+ REBOOT_MODE_BOOTLOADER = 0xFC,
+ REBOOT_MODE_FACTORY = 0xFD,
+ REBOOT_MODE_RECOVERY = 0xFF,
+};
+
+static void exynos_reboot_mode_set(u32 val)
+{
+ int ret;
+ phys_addr_t reboot_cmd_addr = pmu_alive_base + reboot_cmd_offset;
+ u32 mode;
+
+ ret = set_priv_reg(reboot_cmd_addr, val);
+ if (ret) {
+ pr_info("%s(): failed to set addr %pap via set_priv_reg, using regmap\n",
+ __func__, &reboot_cmd_addr);
+ }
+
+ mode = val | BMS_RSBM_VALID;
+ /* [TODO] b/238076990: wait for gbms module is ready
+ ret = gbms_storage_write(GBMS_TAG_RSBM, &mode, sizeof(mode));
+ if (ret < 0)
+ pr_err("%s(): failed to write gbms storage: %d(%d)\n", __func__,
+ GBMS_TAG_RSBM, ret);
+ */
+}
+
+static void exynos_reboot_parse(const char *cmd)
+{
+ if (cmd) {
+ u32 value = U32_MAX;
+ bool force_warm_reboot = false;
+
+ pr_info("Reboot command: '%s'\n", cmd);
+
+ if (!strcmp(cmd, "charge")) {
+ value = REBOOT_MODE_CHARGE;
+ } else if (!strcmp(cmd, "bootloader")) {
+ value = REBOOT_MODE_BOOTLOADER;
+ } else if (!strcmp(cmd, "fastboot")) {
+ value = REBOOT_MODE_FASTBOOT;
+ } else if (!strcmp(cmd, "recovery")) {
+ value = REBOOT_MODE_RECOVERY;
+ } else if (!strcmp(cmd, "dm-verity device corrupted")) {
+ value = REBOOT_MODE_DMVERITY_CORRUPTED;
+ } else if (!strcmp(cmd, "rescue")) {
+ value = REBOOT_MODE_RESCUE;
+ } else if (!strcmp(cmd, "shutdown-thermal") ||
+ !strcmp(cmd, "shutdown,thermal")) {
+ if (force_warm_reboot_on_thermal_shutdown)
+ force_warm_reboot = true;
+ value = REBOOT_MODE_SHUTDOWN_THERMAL;
+ } else if (!strcmp(cmd, "reboot-ab-update")) {
+ value = REBOOT_MODE_AB_UPDATE;
+ } else if (!strcmp(cmd, "from_fastboot") ||
+ !strcmp(cmd, "shell") ||
+ !strcmp(cmd, "userrequested") ||
+ !strcmp(cmd, "userrequested,fastboot") ||
+ !strcmp(cmd, "userrequested,recovery") ||
+ !strcmp(cmd, "userrequested,recovery,ui")) {
+ value = REBOOT_MODE_NORMAL;
+ } else {
+ pr_err("Unknown reboot command: '%s'\n", cmd);
+ }
+
+ /* check for warm_reboot */
+ if (force_warm_reboot)
+ reboot_mode = REBOOT_WARM;
+
+ if (value != U32_MAX)
+ exynos_reboot_mode_set(value);
+ }
+}
+
+static int exynos_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd)
+{
+ exynos_reboot_parse(cmd);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block exynos_reboot_nb = {
+ .notifier_call = exynos_reboot_handler,
+ .priority = INT_MAX,
+};
+
+static int exynos_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd)
+{
+ pr_info("ready to do restart.\n");
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block exynos_restart_nb = {
+ .notifier_call = exynos_restart_handler,
+ .priority = 130,
+};
+
+static int exynos_reboot_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct regmap *pmureg;
+ struct device_node *syscon_np;
+ struct resource res;
+ int err;
+
+ pmureg = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(pmureg)) {
+ dev_err(dev, "Fail to get regmap of PMU\n");
+ return PTR_ERR(pmureg);
+ }
+
+ syscon_np = of_parse_phandle(np, "syscon", 0);
+ if (!syscon_np) {
+ dev_err(dev, "syscon device node not found\n");
+ return -EINVAL;
+ }
+
+ if (of_address_to_resource(syscon_np, 0, &res)) {
+ dev_err(dev, "failed to get syscon base address\n");
+ return -ENOMEM;
+ }
+
+ pmu_alive_base = res.start;
+
+ if (of_property_read_u32(np, "reboot-cmd-offset", &reboot_cmd_offset) < 0) {
+ dev_info(dev, "failed to find reboot-offset property, using default\n");
+ reboot_cmd_offset = EXYNOS_PMU_SYSIP_DAT0;
+ }
+
+ force_warm_reboot_on_thermal_shutdown = of_property_read_bool(np,
+ "force-warm-reboot-on-thermal-shutdown");
+
+ err = register_reboot_notifier(&exynos_reboot_nb);
+ if (err) {
+ dev_err(dev, "cannot register reboot handler (err=%d)\n", err);
+ return err;
+ }
+
+ err = register_restart_handler(&exynos_restart_nb);
+ if (err) {
+ dev_err(dev, "cannot register restart handler (err=%d)\n", err);
+ unregister_reboot_notifier(&exynos_reboot_nb);
+ return err;
+ }
+
+ dev_info(dev, "register restart handler successfully\n");
+
+ return 0;
+}
+
+static const struct of_device_id exynos_reboot_of_match[] = {
+ { .compatible = "samsung,exynos-reboot" },
+ {}
+};
+
+static struct platform_driver exynos_reboot_driver = {
+ .probe = exynos_reboot_probe,
+ .driver = {
+ .name = "exynos-reboot",
+ .of_match_table = exynos_reboot_of_match,
+ },
+};
+module_platform_driver(exynos_reboot_driver);
+
+MODULE_DESCRIPTION("Exynos Reboot driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:exynos-reboot");