Update kernel to ab/13436313

Change-Id: Ie960ba8e4d7293a01c20f67c7d87e460675b4d65
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..7689f15
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+load("//build/kernel/kleaf:kernel.bzl", "kernel_module")
+
+filegroup(
+    name = "reset.kconfig",
+    srcs = [
+        "Kconfig",
+    ],
+    visibility = [
+        "//private/devices/google:__subpackages__",
+        "//private/google-modules/soc/gs:__pkg__",
+    ],
+)
+
+kernel_module(
+    name = "reset",
+    srcs = glob([
+        "**/*.c",
+        "**/*.h",
+    ]) + [
+        "Kbuild",
+        "//private/google-modules/bms:headers",
+        "//private/google-modules/bms/misc:headers",
+        "//private/google-modules/soc/gs:gs_soc_headers",
+    ],
+    outs = [
+        "pixel-reboot.ko",
+    ],
+    kernel_build = "//private/devices/google/common:kernel",
+    visibility = [
+        "//private/devices/google:__subpackages__",
+        "//private/google-modules/soc/gs:__pkg__",
+    ],
+    deps = [
+        "//private/google-modules/bms",
+        "//private/google-modules/soc/gs:gs_soc_module",
+    ],
+)
diff --git a/Kbuild b/Kbuild
new file mode 100644
index 0000000..12ae369
--- /dev/null
+++ b/Kbuild
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += \
+		-I$(KERNEL_SRC)/../private/google-modules/bms \
+
+pixel-reboot-$(CONFIG_SOC_GS101) += pixel-gs101-reboot.o
+pixel-reboot-$(CONFIG_SOC_GS201) += pixel-gs201-reboot.o
+pixel-reboot-$(CONFIG_SOC_ZUMA) += pixel-zuma-reboot.o
+
+obj-$(CONFIG_PIXEL_POWER_REBOOT) += pixel-reboot.o
diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..53277fb
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PIXEL_POWER_REBOOT
+	tristate "Pixel Power Reboot driver"
+	depends on SOC_GS101 || SOC_GS201 || SOC_ZUMA
+	help
+	  Reboot driver support for Pixel devices.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9e5945f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
+M ?= $(shell pwd)
+
+EXTRA_SYMBOLS += $(OUT_DIR)/../private/google-modules/bms/Module.symvers
+
+include $(KERNEL_SRC)/../private/google-modules/soc/gs/Makefile.include
+
+modules modules_install clean:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) \
+	EXTRA_CFLAGS="$(EXTRA_CFLAGS)" KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" $(@)
diff --git a/pixel-gs101-reboot.c b/pixel-gs101-reboot.c
new file mode 100644
index 0000000..1092e75
--- /dev/null
+++ b/pixel-gs101-reboot.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  pixel-reboot.c - Google Pixel SoC reset code
+ *
+ * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd.
+ *
+ * Author: Hyunki Koo <[email protected]>
+ *	   Youngmin Nam <[email protected]>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/input.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/mfd/samsung/s2mpg10.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#if IS_ENABLED(CONFIG_GS_ACPM)
+#include <soc/google/acpm_ipc_ctrl.h>
+#endif
+#include <soc/google/exynos-el3_mon.h>
+#include "../../bms/google_bms.h"
+
+#define EXYNOS_PMU_SYSIP_DAT0		(0x0810)
+
+#define BMS_RSBM_VALID			BIT(31)
+
+#define DUMP_GPR_MODE			(0xDAB)
+
+static struct regmap *pmureg;
+static u32 warm_reboot_offset, warm_reboot_trigger;
+static u32 cold_reboot_offset, cold_reboot_trigger;
+static u32 reboot_cmd_offset;
+static u32 shutdown_offset, shutdown_trigger;
+static u32 dump_gpr_offset;
+static phys_addr_t pmu_alive_base;
+static bool rsbm_supported;
+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 pixel_power_off(void)
+{
+	u32 poweroff_try = 0;
+	int power_gpio = -1;
+	unsigned int keycode = 0;
+	struct device_node *np, *pp;
+
+	np = of_find_node_by_path("/gpio_keys");
+	if (!np)
+		return;
+
+	for_each_child_of_node(np, pp) {
+		if (!of_find_property(pp, "gpios", NULL))
+			continue;
+		of_property_read_u32(pp, "linux,code", &keycode);
+		if (keycode == KEY_POWER) {
+			pr_info("%s: <%u>\n", __func__, keycode);
+			power_gpio = of_get_gpio(pp, 0);
+			break;
+		}
+	}
+
+	of_node_put(np);
+
+	if (!gpio_is_valid(power_gpio)) {
+		pr_err("Couldn't find power key node\n");
+		return;
+	}
+
+	while (1) {
+		/* wait for power button release */
+		if (gpio_get_value(power_gpio)) {
+#if IS_ENABLED(CONFIG_GS_ACPM)
+			exynos_acpm_reboot();
+#endif
+			pr_emerg("Set PS_HOLD Low.\n");
+			rmw_priv_reg(pmu_alive_base + shutdown_offset, shutdown_trigger, 0);
+			++poweroff_try;
+			pr_emerg("Should not reach here! (poweroff_try:%d)\n", poweroff_try);
+		} else {
+			/*
+			 * if power button is not released,
+			 * wait and check TA again
+			 */
+			pr_info("PWR Key is not released.\n");
+		}
+		mdelay(1000);
+	}
+}
+
+static void pixel_reboot_mode_set(u32 val)
+{
+	int ret;
+	u32 mode;
+	phys_addr_t reboot_cmd_addr = pmu_alive_base + reboot_cmd_offset;
+
+	set_priv_reg(reboot_cmd_addr, val);
+
+	if (s2mpg10_get_rev_id() > S2MPG10_EVT0 && rsbm_supported) {
+		mode = val | BMS_RSBM_VALID;
+		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 pixel_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 (!strncmp(cmd, "shutdown-thermal", strlen("shutdown-thermal")) ||
+			   !strncmp(cmd, "shutdown,thermal", strlen("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)
+			pixel_reboot_mode_set(value);
+	}
+}
+
+static int pixel_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd)
+{
+	u32 data;
+	int ret;
+
+	ret = gbms_storage_read(GBMS_TAG_RSBM, &data, sizeof(data));
+	if (ret < 0)
+		pr_err("%s(): failed to read gbms storage: %d(%d)\n", __func__, GBMS_TAG_RSBM, ret);
+
+	rsbm_supported = ret != -ENOENT;
+
+	pixel_reboot_parse(cmd);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block pixel_reboot_nb = {
+	.notifier_call = pixel_reboot_handler,
+	.priority = INT_MAX,
+};
+
+static int pixel_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd)
+{
+#if IS_ENABLED(CONFIG_GS_ACPM)
+	exynos_acpm_reboot();
+#endif
+
+	/* Do S/W Reset */
+	pr_emerg("%s: Pixel SoC reset right now\n", __func__);
+
+	if (reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT)
+		set_priv_reg(pmu_alive_base + dump_gpr_offset, DUMP_GPR_MODE);
+	else
+		set_priv_reg(pmu_alive_base + dump_gpr_offset, 0x0);
+
+	if (s2mpg10_get_rev_id() == S2MPG10_EVT0 || !rsbm_supported ||
+	    reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) {
+		set_priv_reg(pmu_alive_base + warm_reboot_offset, warm_reboot_trigger);
+	} else {
+		pr_emerg("Set PS_HOLD Low.\n");
+		mdelay(2);
+		rmw_priv_reg(pmu_alive_base + cold_reboot_offset, cold_reboot_trigger, 0);
+	}
+
+	while (1)
+		wfi();
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block pixel_restart_nb = {
+	.notifier_call = pixel_restart_handler,
+	.priority = 130,
+};
+
+static int pixel_reboot_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+	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, "swreset-system-offset", &warm_reboot_offset) < 0) {
+		dev_err(dev, "failed to find swreset-system-offset property\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(np, "swreset-system-trigger", &warm_reboot_trigger) < 0) {
+		dev_err(dev, "failed to find swreset-system-trigger property\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(np, "pshold-control-offset", &cold_reboot_offset) < 0) {
+		dev_err(dev, "failed to find pshold-control-offset property\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(np, "pshold-control-trigger", &cold_reboot_trigger) < 0) {
+		dev_err(dev, "failed to find shutdown-trigger property\n");
+		return -EINVAL;
+	}
+
+	shutdown_offset = cold_reboot_offset;
+	shutdown_trigger = cold_reboot_trigger;
+
+	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;
+	}
+
+	if (of_property_read_u32(np, "dump-gpr-offset", &dump_gpr_offset) < 0) {
+		dev_err(dev, "failed to find dump-gpr-offset property\n");
+		return -EINVAL;
+	}
+
+	force_warm_reboot_on_thermal_shutdown = of_property_read_bool(np,
+						"force-warm-reboot-on-thermal-shutdown");
+
+	err = register_reboot_notifier(&pixel_reboot_nb);
+	if (err) {
+		dev_err(dev, "cannot register reboot handler (err=%d)\n", err);
+		return err;
+	}
+
+	err = register_restart_handler(&pixel_restart_nb);
+	if (err) {
+		dev_err(dev, "cannot register restart handler (err=%d)\n", err);
+		unregister_reboot_notifier(&pixel_reboot_nb);
+		return err;
+	}
+
+	pm_power_off = pixel_power_off;
+	dev_info(dev, "register restart handler successfully\n");
+
+	return 0;
+}
+
+static const struct of_device_id pixel_reboot_of_match[] = {
+	{ .compatible = "google,pixel-reboot" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, pixel_reboot_of_match);
+
+static struct platform_driver pixel_reboot_driver = {
+	.probe = pixel_reboot_probe,
+	.driver = {
+		.name = "pixel-reboot",
+		.of_match_table = pixel_reboot_of_match,
+	},
+};
+module_platform_driver(pixel_reboot_driver);
+
+MODULE_DESCRIPTION("Pixel Reboot driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pixel-reboot");
diff --git a/pixel-gs201-reboot.c b/pixel-gs201-reboot.c
new file mode 100644
index 0000000..8faf7bf
--- /dev/null
+++ b/pixel-gs201-reboot.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  pixel-reboot.c - Google Pixel SoC reset code
+ *
+ * Copyright (c) 2019-2021 Samsung Electronics Co., Ltd.
+ *
+ * Author: Hyunki Koo <[email protected]>
+ *	   Youngmin Nam <[email protected]>
+ */
+
+#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/mfd/samsung/s2mpg12.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <soc/google/exynos-el3_mon.h>
+#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 pixel_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;
+	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 pixel_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 (!strncmp(cmd, "shutdown-thermal", strlen("shutdown-thermal")) ||
+			   !strncmp(cmd, "shutdown,thermal", strlen("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)
+			pixel_reboot_mode_set(value);
+	}
+}
+
+static int pixel_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd)
+{
+	pixel_reboot_parse(cmd);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block pixel_reboot_nb = {
+	.notifier_call = pixel_reboot_handler,
+	.priority = INT_MAX,
+};
+
+static int pixel_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 pixel_restart_nb = {
+	.notifier_call = pixel_restart_handler,
+	.priority = 130,
+};
+
+static int pixel_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(&pixel_reboot_nb);
+	if (err) {
+		dev_err(dev, "cannot register reboot handler (err=%d)\n", err);
+		return err;
+	}
+
+	err = register_restart_handler(&pixel_restart_nb);
+	if (err) {
+		dev_err(dev, "cannot register restart handler (err=%d)\n", err);
+		unregister_reboot_notifier(&pixel_reboot_nb);
+		return err;
+	}
+
+	dev_info(dev, "register restart handler successfully\n");
+
+	return 0;
+}
+
+static const struct of_device_id pixel_reboot_of_match[] = {
+	{ .compatible = "google,pixel-reboot" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, pixel_reboot_of_match);
+
+static struct platform_driver pixel_reboot_driver = {
+	.probe = pixel_reboot_probe,
+	.driver = {
+		.name = "pixel-reboot",
+		.of_match_table = pixel_reboot_of_match,
+	},
+};
+module_platform_driver(pixel_reboot_driver);
+
+MODULE_DESCRIPTION("Pixel Reboot driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pixel-reboot");
diff --git a/pixel-zuma-reboot.c b/pixel-zuma-reboot.c
new file mode 100644
index 0000000..e8b0a8a
--- /dev/null
+++ b/pixel-zuma-reboot.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  pixel-reboot.c - Google Pixel SoC reset code
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Author: Hyunki Koo <[email protected]>
+ *	   Youngmin Nam <[email protected]>
+ */
+
+#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>
+#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 pixel_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;
+	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 pixel_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 (!strncmp(cmd, "shutdown-thermal", strlen("shutdown-thermal")) ||
+			   !strncmp(cmd, "shutdown,thermal", strlen("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)
+			pixel_reboot_mode_set(value);
+	}
+}
+
+static int pixel_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd)
+{
+	pixel_reboot_parse(cmd);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block pixel_reboot_nb = {
+	.notifier_call = pixel_reboot_handler,
+	.priority = INT_MAX,
+};
+
+static int pixel_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 pixel_restart_nb = {
+	.notifier_call = pixel_restart_handler,
+	.priority = 130,
+};
+
+static int pixel_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(&pixel_reboot_nb);
+	if (err) {
+		dev_err(dev, "cannot register reboot handler (err=%d)\n", err);
+		return err;
+	}
+
+	err = register_restart_handler(&pixel_restart_nb);
+	if (err) {
+		dev_err(dev, "cannot register restart handler (err=%d)\n", err);
+		unregister_reboot_notifier(&pixel_reboot_nb);
+		return err;
+	}
+
+	dev_info(dev, "register restart handler successfully\n");
+
+	return 0;
+}
+
+static const struct of_device_id pixel_reboot_of_match[] = {
+	{ .compatible = "google,pixel-reboot" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, pixel_reboot_of_match);
+
+static struct platform_driver pixel_reboot_driver = {
+	.probe = pixel_reboot_probe,
+	.driver = {
+		.name = "pixel-reboot",
+		.of_match_table = pixel_reboot_of_match,
+	},
+};
+module_platform_driver(pixel_reboot_driver);
+
+MODULE_DESCRIPTION("Pixel Reboot driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pixel-reboot");