From cce9844ff54707adb5fd2d9c9d595cb0817d90fb Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Tue, 11 Aug 2020 11:25:26 -0700 Subject: [PATCH 01/12] soc: qcom: Remove msm watchdog config Remove qcom watchdog config in preparation for the creation of the watchdog core. Change-Id: I964285a5ad06dc5753756d821bd0f4fc1d664739 Signed-off-by: Branden Bonaby --- arch/arm64/configs/vendor/lahaina_GKI.config | 1 - drivers/soc/qcom/Kconfig | 9 --------- drivers/soc/qcom/Makefile | 1 - 3 files changed, 11 deletions(-) diff --git a/arch/arm64/configs/vendor/lahaina_GKI.config b/arch/arm64/configs/vendor/lahaina_GKI.config index 608323ddb022..ca1fecb63959 100644 --- a/arch/arm64/configs/vendor/lahaina_GKI.config +++ b/arch/arm64/configs/vendor/lahaina_GKI.config @@ -57,7 +57,6 @@ CONFIG_QCOM_LLCC=m CONFIG_QCOM_LAHAINA_LLCC=m CONFIG_QCOM_SHIMA_LLCC=m CONFIG_QCOM_IPCC=m -CONFIG_QCOM_WATCHDOG=m CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_MEMORY_DUMP_V2=m CONFIG_QCOM_SCM=m diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index fda28dc57427..d47cd893fbfb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -783,15 +783,6 @@ config CDSPRM_VTCM_DYNAMIC_DEBUG enabled, the vtcm partition details are sent to the CDSP via rpmsg channel. -config QCOM_WATCHDOG - tristate "Qualcomm Watchdog Support" - depends on ARCH_QCOM - help - This enables the watchdog module. It causes kernel panic if the - watchdog times out. It allows for detection of cpu hangs and - deadlocks. It does not run during the bootup process, so it will - not catch any early lockups. - config QCOM_FORCE_WDOG_BITE_ON_PANIC bool "QCOM force watchdog bite on panic" depends on QCOM_WATCHDOG diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 1d9bf33c9254..cf4c353c7d64 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -74,7 +74,6 @@ ifdef CONFIG_MSM_RPM_SMD obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpm_master_stat.o endif obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o -obj-$(CONFIG_QCOM_WATCHDOG) += qcom_watchdog.o obj-$(CONFIG_MSM_SPCOM) += spcom.o obj-$(CONFIG_QCOM_CDSP_RM) += cdsprm.o obj-$(CONFIG_QCOM_FSA4480_I2C) += fsa4480-i2c.o From 54100ecff9c16644e2b69b4d3611ce603485b7ce Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Tue, 11 Aug 2020 15:55:56 -0700 Subject: [PATCH 02/12] soc: qcom: Rename msm watchdog to wdt core In preparation to change the qcom watchdog to a new core framework that future watchdog drivers will utilize, the name of the file needs to be changed. Due to the number of changes needed in the file we need to rename it to reflect its new purpose. Change-Id: Iac6ad57fbc02866f6716a44d7bb4a62d97e4a810 Signed-off-by: Branden Bonaby --- drivers/soc/qcom/Kconfig | 13 +++++++++++-- drivers/soc/qcom/Makefile | 1 + .../soc/qcom/{qcom_watchdog.c => qcom_wdt_core.c} | 2 +- include/soc/qcom/watchdog.h | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) rename drivers/soc/qcom/{qcom_watchdog.c => qcom_wdt_core.c} (99%) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index d47cd893fbfb..c4b1efe0b9d3 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -783,9 +783,18 @@ config CDSPRM_VTCM_DYNAMIC_DEBUG enabled, the vtcm partition details are sent to the CDSP via rpmsg channel. +config QCOM_WDT_CORE + tristate "Qualcomm Technologies, Inc. Watchdog Support" + depends on ARCH_QCOM + help + This enables the watchdog module. It causes kernel panic if the + watchdog times out. It allows for detection of cpu hangs and + deadlocks. It does not run during the bootup process, so it will + not catch any early lockups. + config QCOM_FORCE_WDOG_BITE_ON_PANIC bool "QCOM force watchdog bite on panic" - depends on QCOM_WATCHDOG + depends on QCOM_WDT_CORE help This forces a watchdog bite when the device restarts due to a kernel panic. On certain MSM SoCs, @@ -794,7 +803,7 @@ config QCOM_FORCE_WDOG_BITE_ON_PANIC config QCOM_WDOG_BITE_EARLY_PANIC bool "QCOM early panic watchdog bite" - depends on QCOM_WATCHDOG && QCOM_FORCE_WDOG_BITE_ON_PANIC + depends on QCOM_WDT_CORE && QCOM_FORCE_WDOG_BITE_ON_PANIC help This forces a watchdog bite early in panic sequence. On certain MSM SoCs, this provides us additional debugging information at the diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index cf4c353c7d64..a18a6cfc768e 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_MSM_QBT_HANDLER) += qbt_handler.o obj-$(CONFIG_QTI_CRYPTO_COMMON) += crypto-qti-common.o obj-$(CONFIG_QTI_CRYPTO_TZ) += crypto-qti-tz.o obj-$(CONFIG_QTI_HW_KEY_MANAGER) += hwkm.o crypto-qti-hwkm.o +obj-$(CONFIG_QCOM_WDT_CORE) += qcom_wdt_core.o ifdef CONFIG_DEBUG_FS obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o endif diff --git a/drivers/soc/qcom/qcom_watchdog.c b/drivers/soc/qcom/qcom_wdt_core.c similarity index 99% rename from drivers/soc/qcom/qcom_watchdog.c rename to drivers/soc/qcom/qcom_wdt_core.c index 918274de9ed9..93808cbad4e4 100644 --- a/drivers/soc/qcom/qcom_watchdog.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -770,7 +770,7 @@ static int __init init_watchdog(void) { return platform_driver_register(&msm_watchdog_driver); } -#if IS_MODULE(CONFIG_QCOM_WATCHDOG) +#if IS_MODULE(CONFIG_QCOM_WDT_CORE) module_init(init_watchdog); #else pure_initcall(init_watchdog); diff --git a/include/soc/qcom/watchdog.h b/include/soc/qcom/watchdog.h index a69ef8b9c7db..358548d73615 100644 --- a/include/soc/qcom/watchdog.h +++ b/include/soc/qcom/watchdog.h @@ -18,7 +18,7 @@ #define WDOG_BITE_EARLY_PANIC 0 #endif -#if IS_ENABLED(CONFIG_QCOM_WATCHDOG) +#if IS_ENABLED(CONFIG_QCOM_WDT_CORE) void msm_trigger_wdog_bite(void); #else static inline void msm_trigger_wdog_bite(void) { } From caf6b22e2260c5b5ba4875c285f82b8a0dd48eb6 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Tue, 11 Aug 2020 16:00:10 -0700 Subject: [PATCH 03/12] arm64: defconfig: Add watchdog core config Add the config for the core watchdog framework. Change-Id: Idfde28614cb5d7ac239c193ff6374ad5b753e366 Signed-off-by: Branden Bonaby --- arch/arm64/configs/vendor/lahaina_GKI.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/vendor/lahaina_GKI.config b/arch/arm64/configs/vendor/lahaina_GKI.config index ca1fecb63959..687052e2abd6 100644 --- a/arch/arm64/configs/vendor/lahaina_GKI.config +++ b/arch/arm64/configs/vendor/lahaina_GKI.config @@ -57,6 +57,7 @@ CONFIG_QCOM_LLCC=m CONFIG_QCOM_LAHAINA_LLCC=m CONFIG_QCOM_SHIMA_LLCC=m CONFIG_QCOM_IPCC=m +CONFIG_QCOM_WDT_CORE=m CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_MEMORY_DUMP_V2=m CONFIG_QCOM_SCM=m From 70ca2d9b8274fc192a4a819c35fefcfdfdfadd5e Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Wed, 5 Aug 2020 08:08:29 -0700 Subject: [PATCH 04/12] soc: qcom: Remove watchdog slack accounting Refactor watchdog core to remove dead code. Specifically, the slack variables from the watchdog data structure and the pet function that uses them. Currently these variables are not used and can be refactored to simply reset the watchdog counter directly. Change-Id: Ie184e2826ed790288a94dfebf578ec99b53831e8 Signed-off-by: Branden Bonaby --- drivers/soc/qcom/qcom_wdt_core.c | 35 ++++---------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c index 93808cbad4e4..312a9b9cc899 100644 --- a/drivers/soc/qcom/qcom_wdt_core.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -63,8 +63,6 @@ struct msm_watchdog_data { bool do_ipi_ping; bool wakeup_irq_enable; unsigned long long last_pet; - unsigned int min_slack_ticks; - unsigned long long min_slack_ns; void *scm_regsave; cpumask_t alive_mask; struct mutex disable_lock; @@ -341,32 +339,6 @@ static ssize_t wdog_pet_time_get(struct device *dev, static DEVICE_ATTR(pet_time, 0400, wdog_pet_time_get, NULL); -static void pet_watchdog(struct msm_watchdog_data *wdog_dd) -{ - int slack, i, count, prev_count = 0; - unsigned long long bark_time_ns = wdog_dd->bark_time * 1000000ULL; - unsigned long long slack_ns; - unsigned long long time_ns; - - for (i = 0; i < 2; i++) { - count = - (__raw_readl(wdog_dd->base + WDT0_STS) >> 1) & 0xFFFFF; - if (count != prev_count) { - prev_count = count; - i = 0; - } - } - slack = ((wdog_dd->bark_time * WDT_HZ) / 1000) - count; - if (slack < wdog_dd->min_slack_ticks) - wdog_dd->min_slack_ticks = slack; - __raw_writel(1, wdog_dd->base + WDT0_RST); - time_ns = sched_clock(); - slack_ns = (wdog_dd->last_pet + bark_time_ns) - time_ns; - if (slack_ns < wdog_dd->min_slack_ns) - wdog_dd->min_slack_ns = slack_ns; - wdog_dd->last_pet = time_ns; -} - static void keep_alive_response(void *info) { struct msm_watchdog_data *wdog_dd = info; @@ -438,7 +410,10 @@ static __ref int watchdog_kthread(void *arg) if (enable) { delay_time = msecs_to_jiffies(wdog_dd->pet_time); - pet_watchdog(wdog_dd); + __raw_writel(1, wdog_dd->base + WDT0_RST); + /* Make sure register write is complete */ + mb(); + wdog_dd->last_pet = sched_clock(); } /* Check again before scheduling * Could have been changed on other cpu @@ -605,8 +580,6 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) } } delay_time = msecs_to_jiffies(wdog_dd->pet_time); - wdog_dd->min_slack_ticks = UINT_MAX; - wdog_dd->min_slack_ns = ULLONG_MAX; timeout = (wdog_dd->bark_time * WDT_HZ)/1000; __raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME); __raw_writel(timeout + 3*WDT_HZ, wdog_dd->base + WDT0_BITE_TIME); From 6bf6e305d32b114976d495a8d51d9df32f3aa6f5 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Wed, 5 Aug 2020 08:46:02 -0700 Subject: [PATCH 05/12] soc: qcom: Remove unused macros Refactor watchdog core to remove unused macro constants. Change-Id: If8517c2c5be519393b8c0394034554533952abd1 Signed-off-by: Branden Bonaby --- drivers/soc/qcom/qcom_wdt_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c index 312a9b9cc899..61ad7015d5e2 100644 --- a/drivers/soc/qcom/qcom_wdt_core.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -27,21 +27,16 @@ #include #include -#define WDT0_ACCSCSSNBARK_INT 0 -#define TCSR_WDT_CFG 0x30 #define WDT0_RST 0x04 #define WDT0_EN 0x08 #define WDT0_STS 0x0C #define WDT0_BARK_TIME 0x10 #define WDT0_BITE_TIME 0x14 -#define WDOG_ABSENT 0 - #define EN 0 #define UNMASKED_INT_EN 1 #define MASK_SIZE 32 -#define MAX_CPU_CTX_SIZE 2048 #define WDT_HZ 32765 static struct msm_watchdog_data *wdog_data; From 2492da5ed79ec66068ddee9d54756f4ea602c2d3 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Wed, 5 Aug 2020 08:55:20 -0700 Subject: [PATCH 06/12] soc: qcom: Remove unused member variables Refactor watchdog core to remove unused member variables in the watchdog data structure. Change-Id: I3a83fdb291064f25721549a3fe70f97629c148b4 Signed-off-by: Branden Bonaby --- drivers/soc/qcom/qcom_wdt_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c index 61ad7015d5e2..c9a833a35eaa 100644 --- a/drivers/soc/qcom/qcom_wdt_core.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -47,18 +47,14 @@ static struct msm_watchdog_data *wdog_data; * Disabled by default on boot. */ struct msm_watchdog_data { - unsigned int __iomem phys_base; - size_t size; void __iomem *base; struct device *dev; unsigned int pet_time; unsigned int bark_time; unsigned int bark_irq; - unsigned int bite_irq; bool do_ipi_ping; bool wakeup_irq_enable; unsigned long long last_pet; - void *scm_regsave; cpumask_t alive_mask; struct mutex disable_lock; bool irq_ppi; @@ -646,7 +642,6 @@ static int msm_wdog_dt_to_pdata(struct platform_device *pdev, } pdata->bark_irq = platform_get_irq(pdev, 0); - pdata->bite_irq = platform_get_irq(pdev, 1); ret = of_property_read_u32(node, "qcom,bark-time", &pdata->bark_time); if (ret) { dev_err(&pdev->dev, "reading bark time failed\n"); From d71acda6dcba348e00523949ddea1eb7ebbd9da1 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Wed, 5 Aug 2020 10:07:37 -0700 Subject: [PATCH 07/12] soc: qcom: Remove watchdog percpu interrupts Refactor watchdog core to remove the bark percpu interrupt as it is deprecated and no longer used by the watchdog. Change-Id: Idbd959a7b42895d72f4f812057304687f7aca948 Signed-off-by: Branden Bonaby --- drivers/soc/qcom/qcom_wdt_core.c | 44 ++++---------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c index c9a833a35eaa..fbbb3205dcc2 100644 --- a/drivers/soc/qcom/qcom_wdt_core.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -57,7 +56,6 @@ struct msm_watchdog_data { unsigned long long last_pet; cpumask_t alive_mask; struct mutex disable_lock; - bool irq_ppi; bool in_panic; struct msm_watchdog_data __percpu **wdog_cpu_dd; struct notifier_block panic_blk; @@ -184,11 +182,7 @@ static void wdog_disable(struct msm_watchdog_data *wdog_dd) __raw_writel(0, wdog_dd->base + WDT0_EN); /* Make sure watchdog is disabled before proceeding */ mb(); - if (wdog_dd->irq_ppi) { - disable_percpu_irq(wdog_dd->bark_irq); - free_percpu_irq(wdog_dd->bark_irq, wdog_dd->wdog_cpu_dd); - } else - devm_free_irq(wdog_dd->dev, wdog_dd->bark_irq, wdog_dd); + devm_free_irq(wdog_dd->dev, wdog_dd->bark_irq, wdog_dd); enable = 0; /*Ensure all cpus see update to enable*/ smp_mb(); @@ -452,8 +446,6 @@ static int msm_watchdog_remove(struct platform_device *pdev) mutex_unlock(&wdog_dd->disable_lock); device_remove_file(wdog_dd->dev, &dev_attr_disable); - if (wdog_dd->irq_ppi) - free_percpu(wdog_dd->wdog_cpu_dd); irq_dispose_mapping(wdog_dd->bark_irq); dev_info(wdog_dd->dev, "MSM Watchdog Exit - Deactivated\n"); del_timer_sync(&wdog_dd->pet_timer); @@ -514,12 +506,6 @@ static irqreturn_t wdog_bark_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t wdog_ppi_bark(int irq, void *dev_id) -{ - struct msm_watchdog_data *wdog_dd = dev_id; - return wdog_bark_handler(irq, wdog_dd); -} - static int init_watchdog_sysfs(struct msm_watchdog_data *wdog_dd) { int error = 0; @@ -546,29 +532,12 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) u64 timeout; int ret; - if (wdog_dd->irq_ppi) { - wdog_dd->wdog_cpu_dd = alloc_percpu(struct msm_watchdog_data *); - if (!wdog_dd->wdog_cpu_dd) { - dev_err(wdog_dd->dev, "fail to allocate cpu data\n"); - return; - } - *raw_cpu_ptr(wdog_dd->wdog_cpu_dd) = wdog_dd; - ret = request_percpu_irq(wdog_dd->bark_irq, wdog_ppi_bark, - "apps_wdog_bark", - wdog_dd->wdog_cpu_dd); - if (ret) { - dev_err(wdog_dd->dev, "failed to request bark irq\n"); - free_percpu(wdog_dd->wdog_cpu_dd); - return; - } - } else { - ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq, + ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq, wdog_bark_handler, IRQF_TRIGGER_RISING, "apps_wdog_bark", wdog_dd); - if (ret) { - dev_err(wdog_dd->dev, "failed to request bark irq\n"); - return; - } + if (ret) { + dev_err(wdog_dd->dev, "failed to request bark irq\n"); + return; } delay_time = msecs_to_jiffies(wdog_dd->pet_time); timeout = (wdog_dd->bark_time * WDT_HZ)/1000; @@ -602,8 +571,6 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) init_watchdog_sysfs(wdog_dd); - if (wdog_dd->irq_ppi) - enable_percpu_irq(wdog_dd->bark_irq, 0); if (!IPI_CORES_IN_LPM) cpu_pm_register_notifier(&wdog_cpu_pm_nb); dev_info(wdog_dd->dev, "MSM Watchdog Initialized\n"); @@ -665,7 +632,6 @@ static int msm_wdog_dt_to_pdata(struct platform_device *pdev, } pdata->wakeup_irq_enable = of_property_read_bool(node, "qcom,wakeup-enable"); - pdata->irq_ppi = irq_is_percpu(pdata->bark_irq); dump_pdata(pdata); return 0; } From 065a08cb14c7010acf863fb9e9d58e78f01cb836 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Wed, 12 Aug 2020 00:34:33 -0700 Subject: [PATCH 08/12] soc: qcom: Create framework for qcom watchdogs Separate core watchdog functionality from the qcom watchdog driver to create a framework for other qcom watchdogs. This will reduce the duplication of code for future watchdog drivers and allow them to take advantage of this framework. Change-Id: I1234af9d92de6841bd09157042a4fbba1745f11b Signed-off-by: Branden Bonaby --- drivers/soc/qcom/Kconfig | 58 +++- drivers/soc/qcom/qcom_wdt_core.c | 480 +++++++++++-------------------- include/soc/qcom/watchdog.h | 118 +++++++- kernel/locking/spinlock_debug.c | 2 +- 4 files changed, 343 insertions(+), 315 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c4b1efe0b9d3..d69c1182e2ec 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -787,10 +787,13 @@ config QCOM_WDT_CORE tristate "Qualcomm Technologies, Inc. Watchdog Support" depends on ARCH_QCOM help - This enables the watchdog module. It causes kernel panic if the - watchdog times out. It allows for detection of cpu hangs and - deadlocks. It does not run during the bootup process, so it will - not catch any early lockups. + This enables the watchdog framework for Qualcomm Technologies, Inc. + devices. It causes a kernel panic if the watchdog times out. It allows + for the detection of cpu hangs and deadlocks. It does not run during the + bootup process, so it will not catch any early lockups. Enabling this + only enables the framework, an individual Qualcomm Technologies, Inc. + watchdog module must be loaded along with this for watchdog + functionality. config QCOM_FORCE_WDOG_BITE_ON_PANIC bool "QCOM force watchdog bite on panic" @@ -811,6 +814,53 @@ config QCOM_WDOG_BITE_EARLY_PANIC later in panic, which permits more of the restart sequence to run (e.g. more dmesg to flushed to console). +config QCOM_WATCHDOG_BARK_TIME + depends on QCOM_WDT_CORE + int "Qualcomm Technologies, Inc. Watchdog bark time in ms" + default 11000 + range 11000 11000 + help + The amount of time, in milliseconds, that should elapse after + a watchdog timer reset before a bark interrupt is sent from the + watchdog. + +config QCOM_WATCHDOG_PET_TIME + depends on QCOM_WDT_CORE + int "Qualcomm Technologies, Inc. Watchdog pet time in ms" + default 9360 + range 9360 9360 + help + The amount of time, in milliseconds, that should elapse before + a watchdog pet is initiated to reset the watchdog timer to 0. + +config QCOM_WATCHDOG_IPI_PING + depends on QCOM_WDT_CORE + bool "Qualcomm Technologies, Inc. Watchdog ipi ping" + default y + help + This boolean flag gives the watchdog driver the ability to send a + keep-alive ping to other cpu's if it is set to 1. Otherwise, when + it is set to 0 no keep alive pings will be sent. + +config QCOM_WATCHDOG_WAKEUP_ENABLE + depends on QCOM_WDT_CORE + bool "Qualcomm Technologies, Inc. Watchdog wakeup enable" + default y + help + This boolean flag allows the non secure watchdog counter to freeze + and unfreeze automatically across the system suspend and resume + path. + +config QCOM_WATCHDOG_USERSPACE_PET + depends on QCOM_WDT_CORE + bool "Qualcomm Technologies, Inc. Watchdog user pet enable" + default n + help + This boolean flag allows enabling the userspace-watchdog feature. + This feature requires userspace to pet the watchdog every in an + interval that matches the time set in the pet-time config. + The feature is supported through device sysfs files. + config MSM_SPCOM depends on QCOM_GLINK tristate "Secure Processor Communication over RPMSG" diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c index fbbb3205dcc2..28255c48b6a8 100644 --- a/drivers/soc/qcom/qcom_wdt_core.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -2,103 +2,29 @@ /* * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ - -#include -#include #include #include -#include #include +#include #include #include #include -#include -#include -#include -#include #include #include #include #include #include -#include -#include #include +#include #include +#include +#include -#define WDT0_RST 0x04 -#define WDT0_EN 0x08 -#define WDT0_STS 0x0C -#define WDT0_BARK_TIME 0x10 -#define WDT0_BITE_TIME 0x14 - -#define EN 0 -#define UNMASKED_INT_EN 1 - -#define MASK_SIZE 32 -#define WDT_HZ 32765 +#define MASK_SIZE 32 static struct msm_watchdog_data *wdog_data; -/* - * user_pet_enable: - * Require userspace to write to a sysfs file every pet_time milliseconds. - * Disabled by default on boot. - */ -struct msm_watchdog_data { - void __iomem *base; - struct device *dev; - unsigned int pet_time; - unsigned int bark_time; - unsigned int bark_irq; - bool do_ipi_ping; - bool wakeup_irq_enable; - unsigned long long last_pet; - cpumask_t alive_mask; - struct mutex disable_lock; - bool in_panic; - struct msm_watchdog_data __percpu **wdog_cpu_dd; - struct notifier_block panic_blk; - struct notifier_block restart_blk; - - bool enabled; - bool user_pet_enabled; - - struct task_struct *watchdog_task; - struct timer_list pet_timer; - wait_queue_head_t pet_complete; - - bool timer_expired; - bool user_pet_complete; - unsigned long long timer_fired; - unsigned long long thread_start; - unsigned long long ping_start[NR_CPUS]; - unsigned long long ping_end[NR_CPUS]; - int cpu_idle_pc_state[NR_CPUS]; -}; - -/* - * On the kernel command line specify - * qcom_watchdog.enable=1 to enable the watchdog - * By default watchdog is turned on - */ -static int enable = 1; -module_param(enable, int, 0000); - -/* - * Watchdog ipi optimization: - * Does not ping cores in low power mode at pet time to save power. - * This feature is enabled by default. - * - * Can be turned off, by enabling CONFIG_QCOM_WDOG_IPI_ENABLE. - */ -#ifdef CONFIG_QCOM_WDOG_IPI_ENABLE -#define IPI_CORES_IN_LPM 1 -#else -#define IPI_CORES_IN_LPM 0 -#endif - -static void dump_cpu_alive_mask(struct msm_watchdog_data *wdog_dd) +static void qcom_wdt_dump_cpu_alive_mask(struct msm_watchdog_data *wdog_dd) { static char alive_mask_buf[MASK_SIZE]; @@ -109,92 +35,97 @@ static void dump_cpu_alive_mask(struct msm_watchdog_data *wdog_dd) } #ifdef CONFIG_PM_SLEEP -static int msm_watchdog_suspend(struct device *dev) +/** + * qcom_wdt_suspend() - Suspends qcom watchdog functionality. + * + * @dev: qcom watchdog device structure + * + * All watchdogs should have the ability to be suspended, this + * will cause the watchdog counter to be frozen. + * + */ +int qcom_wdt_suspend(struct device *dev) { struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev); - if (!enable) + + if (!wdog_dd) return 0; - __raw_writel(1, wdog_dd->base + WDT0_RST); + wdog_dd->ops->reset_wdt(wdog_dd); if (wdog_dd->wakeup_irq_enable) { - /* Make sure register write is complete before proceeding */ - mb(); wdog_dd->last_pet = sched_clock(); return 0; } - __raw_writel(0, wdog_dd->base + WDT0_EN); - /* Make sure watchdog is suspended before setting enable */ - mb(); + wdog_dd->ops->disable_wdt(wdog_dd); wdog_dd->enabled = false; wdog_dd->last_pet = sched_clock(); return 0; } -#endif +EXPORT_SYMBOL(qcom_wdt_suspend); -#ifdef CONFIG_PM_SLEEP -static int msm_watchdog_resume(struct device *dev) +/** + * qcom_wdt_resume() - Resumes qcom watchdog after a suspend. + * + * @dev: qcom watchdog device structure + * + * All watchdogs should have the ability to be resumed after a suspend. + * This will cause the watchdog counter to be reset and resumed. + * + */ +int qcom_wdt_resume(struct device *dev) { struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev); - if (!enable) + + if (!wdog_dd) return 0; if (wdog_dd->wakeup_irq_enable) { - __raw_writel(1, wdog_dd->base + WDT0_RST); - /* Make sure register write is complete before proceeding */ - mb(); + wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); return 0; } - __raw_writel(1, wdog_dd->base + WDT0_EN); - __raw_writel(1, wdog_dd->base + WDT0_RST); - /* Make sure watchdog is reset before setting enable */ - mb(); + wdog_dd->ops->enable_wdt(1, wdog_dd); + wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->enabled = true; wdog_dd->last_pet = sched_clock(); return 0; } +EXPORT_SYMBOL(qcom_wdt_resume); #endif -static int panic_wdog_handler(struct notifier_block *this, +static int qcom_wdt_panic_handler(struct notifier_block *this, unsigned long event, void *ptr) { struct msm_watchdog_data *wdog_dd = container_of(this, struct msm_watchdog_data, panic_blk); + wdog_dd->in_panic = true; if (WDOG_BITE_EARLY_PANIC) { pr_info("Triggering early bite\n"); - msm_trigger_wdog_bite(); + qcom_wdt_trigger_bite(); } if (panic_timeout == 0) { - __raw_writel(0, wdog_dd->base + WDT0_EN); - /* Make sure watchdog is enabled before notifying the caller */ - mb(); + wdog_dd->ops->disable_wdt(wdog_dd); } else { - __raw_writel(WDT_HZ * (panic_timeout + 10), - wdog_dd->base + WDT0_BARK_TIME); - __raw_writel(WDT_HZ * (panic_timeout + 10), - wdog_dd->base + WDT0_BITE_TIME); - __raw_writel(1, wdog_dd->base + WDT0_RST); + wdog_dd->ops->set_bark_time((panic_timeout + 10) * 1000, + wdog_dd); + wdog_dd->ops->set_bite_time((panic_timeout + 10) * 1000, + wdog_dd); + wdog_dd->ops->reset_wdt(wdog_dd); } return NOTIFY_DONE; } -static void wdog_disable(struct msm_watchdog_data *wdog_dd) +static void qcom_wdt_disable(struct msm_watchdog_data *wdog_dd) { - __raw_writel(0, wdog_dd->base + WDT0_EN); - /* Make sure watchdog is disabled before proceeding */ - mb(); + wdog_dd->ops->disable_wdt(wdog_dd); devm_free_irq(wdog_dd->dev, wdog_dd->bark_irq, wdog_dd); - enable = 0; + wdog_dd->enabled = false; /*Ensure all cpus see update to enable*/ smp_mb(); atomic_notifier_chain_unregister(&panic_notifier_list, &wdog_dd->panic_blk); del_timer_sync(&wdog_dd->pet_timer); - /* may be suspended after the first write above */ - __raw_writel(0, wdog_dd->base + WDT0_EN); - /* Make sure watchdog is disabled before setting enable */ - mb(); - wdog_dd->enabled = false; - dev_err(wdog_dd->dev, "MSM Apps Watchdog deactivated\n"); + wdog_dd->ops->disable_wdt(wdog_dd); + dev_err(wdog_dd->dev, "QCOM Apps Watchdog deactivated\n"); } static int restart_wdog_handler(struct notifier_block *this, @@ -208,24 +139,26 @@ static int restart_wdog_handler(struct notifier_block *this, * device will take the usual restart path. */ pr_info("Triggering late bite\n"); - msm_trigger_wdog_bite(); + qcom_wdt_trigger_bite(); } return NOTIFY_DONE; } -static ssize_t wdog_disable_get(struct device *dev, +static ssize_t qcom_wdt_disable_get(struct device *dev, struct device_attribute *attr, char *buf) { struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev); int ret; + int disable_val; mutex_lock(&wdog_dd->disable_lock); - ret = scnprintf(buf, PAGE_SIZE, "%d\n", enable == 0 ? 1 : 0); + disable_val = wdog_dd->enabled ? 0 : 1; + ret = scnprintf(buf, PAGE_SIZE, "%d\n", disable_val); mutex_unlock(&wdog_dd->disable_lock); return ret; } -static ssize_t wdog_disable_set(struct device *dev, +static ssize_t qcom_wdt_disable_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -240,21 +173,18 @@ static ssize_t wdog_disable_set(struct device *dev, } if (disable == 1) { mutex_lock(&wdog_dd->disable_lock); - if (enable == 0) { + if (!wdog_dd->enabled) { dev_err(wdog_dd->dev, "MSM Apps Watchdog already disabled\n"); mutex_unlock(&wdog_dd->disable_lock); return count; } - disable = 1; - ret = qcom_scm_sec_wdog_deactivate(); if (ret) { dev_err(wdog_dd->dev, - "Failed to deactivate secure wdog\n"); - mutex_unlock(&wdog_dd->disable_lock); - return -EIO; + "Failed to deactivate secure wdog, ret = %d\n", + ret); } - wdog_disable(wdog_dd); + qcom_wdt_disable(wdog_dd); mutex_unlock(&wdog_dd->disable_lock); } else { pr_err("invalid operation, only disable = 1 supported\n"); @@ -263,7 +193,7 @@ static ssize_t wdog_disable_set(struct device *dev, return count; } -static DEVICE_ATTR(disable, 0600, wdog_disable_get, wdog_disable_set); +static DEVICE_ATTR(disable, 0600, qcom_wdt_disable_get, qcom_wdt_disable_set); /* * Userspace Watchdog Support: @@ -274,13 +204,13 @@ static DEVICE_ATTR(disable, 0600, wdog_disable_get, wdog_disable_set); * Userspace may disable this requirement by writing 0 to this same * file. */ -static void __wdog_user_pet(struct msm_watchdog_data *wdog_dd) +static void __qcom_wdt_user_pet(struct msm_watchdog_data *wdog_dd) { wdog_dd->user_pet_complete = true; wake_up(&wdog_dd->pet_complete); } -static ssize_t wdog_user_pet_enabled_get(struct device *dev, +static ssize_t qcom_wdt_user_pet_enabled_get(struct device *dev, struct device_attribute *attr, char *buf) { struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev); @@ -291,7 +221,7 @@ static ssize_t wdog_user_pet_enabled_get(struct device *dev, return ret; } -static ssize_t wdog_user_pet_enabled_set(struct device *dev, +static ssize_t qcom_wdt_user_pet_enabled_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -304,15 +234,15 @@ static ssize_t wdog_user_pet_enabled_set(struct device *dev, return ret; } - __wdog_user_pet(wdog_dd); + __qcom_wdt_user_pet(wdog_dd); return count; } -static DEVICE_ATTR(user_pet_enabled, 0600, wdog_user_pet_enabled_get, - wdog_user_pet_enabled_set); +static DEVICE_ATTR(user_pet_enabled, 0600, qcom_wdt_user_pet_enabled_get, + qcom_wdt_user_pet_enabled_set); -static ssize_t wdog_pet_time_get(struct device *dev, +static ssize_t qcom_wdt_pet_time_get(struct device *dev, struct device_attribute *attr, char *buf) { struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev); @@ -322,9 +252,9 @@ static ssize_t wdog_pet_time_get(struct device *dev, return ret; } -static DEVICE_ATTR(pet_time, 0400, wdog_pet_time_get, NULL); +static DEVICE_ATTR(pet_time, 0400, qcom_wdt_pet_time_get, NULL); -static void keep_alive_response(void *info) +static void qcom_wdt_keep_alive_response(void *info) { struct msm_watchdog_data *wdog_dd = info; int cpu = smp_processor_id(); @@ -339,7 +269,7 @@ static void keep_alive_response(void *info) * If this function does not return, it implies one of the * other cpu's is not responsive. */ -static void ping_other_cpus(struct msm_watchdog_data *wdog_dd) +static void qcom_wdt_ping_other_cpus(struct msm_watchdog_data *wdog_dd) { int cpu; @@ -347,15 +277,16 @@ static void ping_other_cpus(struct msm_watchdog_data *wdog_dd) /* Make sure alive mask is cleared and set in order */ smp_mb(); for_each_cpu(cpu, cpu_online_mask) { - if (!wdog_data->cpu_idle_pc_state[cpu]) { + if (!wdog_dd->cpu_idle_pc_state[cpu]) { wdog_dd->ping_start[cpu] = sched_clock(); - smp_call_function_single(cpu, keep_alive_response, + smp_call_function_single(cpu, + qcom_wdt_keep_alive_response, wdog_dd, 1); } } } -static void pet_task_wakeup(struct timer_list *t) +static void qcom_wdt_pet_task_wakeup(struct timer_list *t) { struct msm_watchdog_data *wdog_dd = from_timer(wdog_dd, t, pet_timer); @@ -364,7 +295,7 @@ static void pet_task_wakeup(struct timer_list *t) wake_up(&wdog_dd->pet_complete); } -static __ref int watchdog_kthread(void *arg) +static __ref int qcom_wdt_kthread(void *arg) { struct msm_watchdog_data *wdog_dd = arg; unsigned long delay_time = 0; @@ -383,7 +314,7 @@ static __ref int watchdog_kthread(void *arg) wdog_dd->ping_start[cpu] = wdog_dd->ping_end[cpu] = 0; if (wdog_dd->do_ipi_ping) - ping_other_cpus(wdog_dd); + qcom_wdt_ping_other_cpus(wdog_dd); do { ret = wait_event_interruptible(wdog_dd->pet_complete, @@ -393,86 +324,89 @@ static __ref int watchdog_kthread(void *arg) wdog_dd->timer_expired = false; wdog_dd->user_pet_complete = !wdog_dd->user_pet_enabled; - if (enable) { + if (wdog_dd->enabled) { delay_time = msecs_to_jiffies(wdog_dd->pet_time); - __raw_writel(1, wdog_dd->base + WDT0_RST); - /* Make sure register write is complete */ - mb(); + wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); } /* Check again before scheduling * Could have been changed on other cpu */ - if(!kthread_should_stop()) + if (!kthread_should_stop()) mod_timer(&wdog_dd->pet_timer, jiffies + delay_time); } return 0; } -static int wdog_cpu_pm_notify(struct notifier_block *self, +static int qcom_wdt_cpu_pm_notify(struct notifier_block *this, unsigned long action, void *v) { + struct msm_watchdog_data *wdog_dd = container_of(this, + struct msm_watchdog_data, wdog_cpu_pm_nb); int cpu; cpu = raw_smp_processor_id(); switch (action) { case CPU_PM_ENTER: - wdog_data->cpu_idle_pc_state[cpu] = 1; + wdog_dd->cpu_idle_pc_state[cpu] = 1; break; case CPU_PM_ENTER_FAILED: case CPU_PM_EXIT: - wdog_data->cpu_idle_pc_state[cpu] = 0; + wdog_dd->cpu_idle_pc_state[cpu] = 0; break; } return NOTIFY_OK; } -static struct notifier_block wdog_cpu_pm_nb = { - .notifier_call = wdog_cpu_pm_notify, -}; - -static int msm_watchdog_remove(struct platform_device *pdev) +/** + * qcom_wdt_remove() - Removes the watchdog and stops it's kthread. + * + * @pdev: watchdog platform_device + * + * Upon the removal of the module all watchdog data along with the kthread + * will be cleaned up and the watchdog device will be removed from memory. + * + */ +int qcom_wdt_remove(struct platform_device *pdev) { struct msm_watchdog_data *wdog_dd = platform_get_drvdata(pdev); if (!IPI_CORES_IN_LPM) - cpu_pm_unregister_notifier(&wdog_cpu_pm_nb); + cpu_pm_unregister_notifier(&wdog_dd->wdog_cpu_pm_nb); mutex_lock(&wdog_dd->disable_lock); - if (enable) - wdog_disable(wdog_dd); + if (wdog_dd->enabled) + qcom_wdt_disable(wdog_dd); mutex_unlock(&wdog_dd->disable_lock); device_remove_file(wdog_dd->dev, &dev_attr_disable); irq_dispose_mapping(wdog_dd->bark_irq); - dev_info(wdog_dd->dev, "MSM Watchdog Exit - Deactivated\n"); + dev_info(wdog_dd->dev, "QCOM Apps Watchdog Exit - Deactivated\n"); del_timer_sync(&wdog_dd->pet_timer); wdog_dd->timer_expired = true; wdog_dd->user_pet_complete = true; kthread_stop(wdog_dd->watchdog_task); return 0; } +EXPORT_SYMBOL(qcom_wdt_remove); -void msm_trigger_wdog_bite(void) +/** + * qcom_wdt_trigger_bite - Executes a watchdog bite. + * + * Return: function will not return, to allow for the bite to occur + */ +void qcom_wdt_trigger_bite(void) { if (!wdog_data) return; - dev_err(wdog_data->dev, "Causing a watchdog bite!\n"); - __raw_writel(1, wdog_data->base + WDT0_BITE_TIME); - /* Mke sure bite time is written before we reset */ - mb(); - __raw_writel(1, wdog_data->base + WDT0_RST); - /* Make sure we wait only after reset */ - mb(); + dev_err(wdog_data->dev, "Causing a QCOM Apps Watchdog bite!\n"); + wdog_data->ops->show_wdt_status(wdog_data); + wdog_data->ops->set_bite_time(1, wdog_data); + wdog_data->ops->reset_wdt(wdog_data); /* Delay to make sure bite occurs */ mdelay(10000); - dev_err(wdog_data->dev, "Wdog - STS: 0x%x, CTL: 0x%x, BARK TIME: 0x%x, BITE TIME: 0x%x\n", - __raw_readl(wdog_data->base + WDT0_STS), - __raw_readl(wdog_data->base + WDT0_EN), - __raw_readl(wdog_data->base + WDT0_BARK_TIME), - __raw_readl(wdog_data->base + WDT0_BITE_TIME)); /* * This function induces the non-secure bite and control * should not return to the calling function. Non-secure @@ -484,68 +418,65 @@ void msm_trigger_wdog_bite(void) udelay(1); } -EXPORT_SYMBOL(msm_trigger_wdog_bite); +EXPORT_SYMBOL(qcom_wdt_trigger_bite); -static irqreturn_t wdog_bark_handler(int irq, void *dev_id) +static irqreturn_t qcom_wdt_bark_handler(int irq, void *dev_id) { struct msm_watchdog_data *wdog_dd = dev_id; unsigned long nanosec_rem; unsigned long long t = sched_clock(); nanosec_rem = do_div(t, 1000000000); - dev_info(wdog_dd->dev, "Watchdog bark! Now = %lu.%06lu\n", + dev_info(wdog_dd->dev, "QCOM Apps Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t, nanosec_rem / 1000); nanosec_rem = do_div(wdog_dd->last_pet, 1000000000); - dev_info(wdog_dd->dev, "Watchdog last pet at %lu.%06lu\n", + dev_info(wdog_dd->dev, "QCOM Apps Watchdog last pet at %lu.%06lu\n", (unsigned long) wdog_dd->last_pet, nanosec_rem / 1000); if (wdog_dd->do_ipi_ping) - dump_cpu_alive_mask(wdog_dd); - msm_trigger_wdog_bite(); + qcom_wdt_dump_cpu_alive_mask(wdog_dd); + + qcom_wdt_trigger_bite(); return IRQ_HANDLED; } -static int init_watchdog_sysfs(struct msm_watchdog_data *wdog_dd) +static int qcom_wdt_init_sysfs(struct msm_watchdog_data *wdog_dd) { int error = 0; error |= device_create_file(wdog_dd->dev, &dev_attr_disable); - - if (of_property_read_bool(wdog_dd->dev->of_node, - "qcom,userspace-watchdog")) { + if (QCOM_WATCHDOG_USERSPACE_PET) { error |= device_create_file(wdog_dd->dev, &dev_attr_pet_time); error |= device_create_file(wdog_dd->dev, &dev_attr_user_pet_enabled); } - if (error) dev_err(wdog_dd->dev, "cannot create sysfs attribute\n"); return error; } -static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) +static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd, + struct platform_device *pdev) { unsigned long delay_time; uint32_t val; - u64 timeout; int ret; ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq, - wdog_bark_handler, IRQF_TRIGGER_RISING, - "apps_wdog_bark", wdog_dd); + qcom_wdt_bark_handler, + IRQF_TRIGGER_RISING, + "apps_wdog_bark", wdog_dd); if (ret) { dev_err(wdog_dd->dev, "failed to request bark irq\n"); - return; + return -EINVAL; } delay_time = msecs_to_jiffies(wdog_dd->pet_time); - timeout = (wdog_dd->bark_time * WDT_HZ)/1000; - __raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME); - __raw_writel(timeout + 3*WDT_HZ, wdog_dd->base + WDT0_BITE_TIME); - + wdog_dd->ops->set_bark_time(wdog_dd->bark_time, wdog_dd); + wdog_dd->ops->set_bite_time(wdog_dd->bark_time + 3 * 1000, wdog_dd); wdog_dd->panic_blk.priority = WDOG_BITE_EARLY_PANIC ? INT_MAX - 1 : 0; - wdog_dd->panic_blk.notifier_call = panic_wdog_handler; + wdog_dd->panic_blk.notifier_call = qcom_wdt_panic_handler; atomic_notifier_chain_register(&panic_notifier_list, &wdog_dd->panic_blk); wdog_dd->restart_blk.priority = 255; @@ -557,31 +488,33 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) wdog_dd->user_pet_complete = true; wdog_dd->user_pet_enabled = false; wake_up_process(wdog_dd->watchdog_task); - timer_setup(&wdog_dd->pet_timer, pet_task_wakeup, 0); + timer_setup(&wdog_dd->pet_timer, qcom_wdt_pet_task_wakeup, 0); wdog_dd->pet_timer.expires = jiffies + delay_time; add_timer(&wdog_dd->pet_timer); - val = BIT(EN); if (wdog_dd->wakeup_irq_enable) val |= BIT(UNMASKED_INT_EN); - __raw_writel(val, wdog_dd->base + WDT0_EN); - __raw_writel(1, wdog_dd->base + WDT0_RST); + ret = wdog_dd->ops->enable_wdt(val, wdog_dd); + if (ret) { + dev_err(wdog_dd->dev, "Failed Initializing QCOM Apps Watchdog\n"); + return ret; + } + wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); wdog_dd->enabled = true; - init_watchdog_sysfs(wdog_dd); + qcom_wdt_init_sysfs(wdog_dd); - if (!IPI_CORES_IN_LPM) - cpu_pm_register_notifier(&wdog_cpu_pm_nb); - dev_info(wdog_dd->dev, "MSM Watchdog Initialized\n"); + if (!IPI_CORES_IN_LPM) { + wdog_dd->wdog_cpu_pm_nb.notifier_call = qcom_wdt_cpu_pm_notify; + cpu_pm_register_notifier(&wdog_dd->wdog_cpu_pm_nb); + } + dev_info(wdog_dd->dev, "QCOM Apps Watchdog Initialized\n"); + + return 0; } -static const struct of_device_id msm_wdog_match_table[] = { - { .compatible = "qcom,msm-watchdog" }, - {} -}; - -static void dump_pdata(struct msm_watchdog_data *pdata) +static void qcom_wdt_dump_pdata(struct msm_watchdog_data *pdata) { dev_dbg(pdata->dev, "wdog bark_time %d", pdata->bark_time); dev_dbg(pdata->dev, "wdog pet_time %d", pdata->pet_time); @@ -590,78 +523,49 @@ static void dump_pdata(struct msm_watchdog_data *pdata) pdata->base); } -static int msm_wdog_dt_to_pdata(struct platform_device *pdev, - struct msm_watchdog_data *pdata) +static void qcom_wdt_dt_to_pdata(struct platform_device *pdev, + struct msm_watchdog_data *pdata) { - struct device_node *node = pdev->dev.of_node; - struct resource *res; - int ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wdt-base"); - if (!res) - return -ENODEV; - - pdata->base = devm_ioremap_resource(&pdev->dev, res); - if (!pdata->base) { - dev_err(&pdev->dev, "%s cannot map wdog register space\n", - __func__); - return -ENXIO; - } - pdata->bark_irq = platform_get_irq(pdev, 0); - ret = of_property_read_u32(node, "qcom,bark-time", &pdata->bark_time); - if (ret) { - dev_err(&pdev->dev, "reading bark time failed\n"); - return -ENXIO; - } - ret = of_property_read_u32(node, "qcom,pet-time", &pdata->pet_time); - if (ret) { - dev_err(&pdev->dev, "reading pet time failed\n"); - return -ENXIO; - } - pdata->do_ipi_ping = of_property_read_bool(node, "qcom,ipi-ping"); - if (!pdata->bark_time) { - dev_err(&pdev->dev, "%s watchdog bark time not setup\n", - __func__); - return -ENXIO; - } - if (!pdata->pet_time) { - dev_err(&pdev->dev, "%s watchdog pet time not setup\n", - __func__); - return -ENXIO; - } - pdata->wakeup_irq_enable = of_property_read_bool(node, - "qcom,wakeup-enable"); - dump_pdata(pdata); - return 0; + pdata->bark_time = QCOM_WATCHDOG_BARK_TIME; + pdata->pet_time = QCOM_WATCHDOG_PET_TIME; + pdata->do_ipi_ping = QCOM_WATCHDOG_IPI_PING; + pdata->wakeup_irq_enable = QCOM_WATCHDOG_WAKEUP_ENABLE; + qcom_wdt_dump_pdata(pdata); } -static int msm_watchdog_probe(struct platform_device *pdev) +/** + * qcom_wdt_register() - Creates QCOM Apps watchdog device. + * + * @pdev: watchdog platform_device + * @ops: watchdog operations + * + * All QCOM Apps watchdogs should be created the same way, this acts + * as a framework for this purpose. + * + * 0 on success, negative errno on failure. + */ +int qcom_wdt_register(struct platform_device *pdev, + struct msm_watchdog_data *wdog_dd, + char *wdog_dd_name) { - struct msm_watchdog_data *wdog_dd; struct md_region md_entry; int ret; - if (!enable) - return -ENODEV; - wdog_dd = devm_kzalloc(&pdev->dev, sizeof(*wdog_dd), GFP_KERNEL); - if (!wdog_dd) - return -ENOMEM; - ret = msm_wdog_dt_to_pdata(pdev, wdog_dd); - if (ret) - goto err; - + qcom_wdt_dt_to_pdata(pdev, wdog_dd); wdog_data = wdog_dd; wdog_dd->dev = &pdev->dev; platform_set_drvdata(pdev, wdog_dd); cpumask_clear(&wdog_dd->alive_mask); - wdog_dd->watchdog_task = kthread_create(watchdog_kthread, wdog_dd, - "msm_watchdog"); + wdog_dd->watchdog_task = kthread_create(qcom_wdt_kthread, wdog_dd, + wdog_dd_name); if (IS_ERR(wdog_dd->watchdog_task)) { ret = PTR_ERR(wdog_dd->watchdog_task); goto err; } - init_watchdog_data(wdog_dd); + ret = qcom_wdt_init(wdog_dd, pdev); + if (ret) + goto err; /* Add wdog info to minidump table */ strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name)); @@ -674,42 +578,4 @@ static int msm_watchdog_probe(struct platform_device *pdev) err: return ret; } - -#ifdef CONFIG_PM_SLEEP -static const struct dev_pm_ops msm_watchdog_dev_pm_ops = { - .suspend_noirq = msm_watchdog_suspend, - .resume_noirq = msm_watchdog_resume, -}; -#else -static const struct dev_pm_ops msm_watchdog_dev_pm_ops = { -}; -#endif - -static struct platform_driver msm_watchdog_driver = { - .probe = msm_watchdog_probe, - .remove = msm_watchdog_remove, - .driver = { - .name = "msm_watchdog", - .pm = &msm_watchdog_dev_pm_ops, - .of_match_table = msm_wdog_match_table, - }, -}; - -static int __init init_watchdog(void) -{ - return platform_driver_register(&msm_watchdog_driver); -} -#if IS_MODULE(CONFIG_QCOM_WDT_CORE) -module_init(init_watchdog); -#else -pure_initcall(init_watchdog); -#endif - -static __exit void exit_watchdog(void) -{ - platform_driver_unregister(&msm_watchdog_driver); -} -module_exit(exit_watchdog); - -MODULE_DESCRIPTION("MSM Watchdog Driver"); -MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL(qcom_wdt_register); diff --git a/include/soc/qcom/watchdog.h b/include/soc/qcom/watchdog.h index 358548d73615..4040a22f4edf 100644 --- a/include/soc/qcom/watchdog.h +++ b/include/soc/qcom/watchdog.h @@ -18,10 +18,122 @@ #define WDOG_BITE_EARLY_PANIC 0 #endif -#if IS_ENABLED(CONFIG_QCOM_WDT_CORE) -void msm_trigger_wdog_bite(void); +/* + * Watchdog ipi optimization: + * Does not ping cores in low power mode at pet time to save power. + * This feature is enabled by default. + * + * Can be turned off, by enabling CONFIG_QCOM_WDOG_IPI_ENABLE. + */ +#ifdef CONFIG_QCOM_WDOG_IPI_ENABLE +#define IPI_CORES_IN_LPM 1 #else -static inline void msm_trigger_wdog_bite(void) { } +#define IPI_CORES_IN_LPM 0 +#endif + +#if IS_ENABLED(CONFIG_QCOM_WDT_CORE) +#include + +/** + * The enable constant that can be used between the core framework and the + * watchdog driver. + */ +#define EN 0 +#define UNMASKED_INT_EN 1 + +/* Watchdog property values */ +#define QCOM_WATCHDOG_BARK_TIME CONFIG_QCOM_WATCHDOG_BARK_TIME +#define QCOM_WATCHDOG_PET_TIME CONFIG_QCOM_WATCHDOG_PET_TIME + +#ifdef CONFIG_QCOM_WATCHDOG_IPI_PING +#define QCOM_WATCHDOG_IPI_PING 1 +#else +#define QCOM_WATCHDOG_IPI_PING 0 +#endif + +#ifdef CONFIG_QCOM_WATCHDOG_WAKEUP_ENABLE +#define QCOM_WATCHDOG_WAKEUP_ENABLE 1 +#else +#define QCOM_WATCHDOG_WAKEUP_ENABLE 0 +#endif + +#ifdef CONFIG_QCOM_WATCHDOG_USERSPACE_PET +#define QCOM_WATCHDOG_USERSPACE_PET 1 +#else +#define QCOM_WATCHDOG_USERSPACE_PET 0 +#endif + +struct qcom_wdt_ops; +struct msm_watchdog_data; + +/** qcom_wdt_ops - The msm-watchdog-devices operations + * + * @set_bark_time: The routine for setting the watchdog bark time. + * @set_bite_time: The routine for setting the watchdog bite time. + * @reset_wdt: The routine for resetting the watchdog timer. + * @enable_wdt: The routine for enabling the watchdog. + * @disable_wdt: The routine for disabling the watchdog. + * @show_wdt_status: The routine that shows the status of the watchdog. + * + * The qcom_wdt_ops structure contains a list of operations that are + * used to control the watchdog. + */ +struct qcom_wdt_ops { + int (*set_bark_time)(u32 time, struct msm_watchdog_data *wdog_dd); + int (*set_bite_time)(u32 time, struct msm_watchdog_data *wdog_dd); + int (*reset_wdt)(struct msm_watchdog_data *wdog_dd); + int (*enable_wdt)(u32 val, struct msm_watchdog_data *wdog_dd); + int (*disable_wdt)(struct msm_watchdog_data *wdog_dd); + int (*show_wdt_status)(struct msm_watchdog_data *wdog_dd); +}; + +/* + * user_pet_enable: + * Require userspace to write to a sysfs file every pet_time milliseconds. + * Disabled by default on boot. + */ +struct msm_watchdog_data { + void __iomem *base; + struct device *dev; + struct qcom_wdt_ops *ops; + unsigned int pet_time; + unsigned int bark_time; + unsigned int bark_irq; + bool do_ipi_ping; + bool in_panic; + bool wakeup_irq_enable; + unsigned long long last_pet; + cpumask_t alive_mask; + struct mutex disable_lock; + struct notifier_block panic_blk; + struct notifier_block wdog_cpu_pm_nb; + struct notifier_block restart_blk; + + bool enabled; + bool user_pet_enabled; + + struct task_struct *watchdog_task; + struct timer_list pet_timer; + wait_queue_head_t pet_complete; + + bool timer_expired; + bool user_pet_complete; + unsigned long long timer_fired; + unsigned long long thread_start; + unsigned long long ping_start[NR_CPUS]; + unsigned long long ping_end[NR_CPUS]; + int cpu_idle_pc_state[NR_CPUS]; +}; + +extern void qcom_wdt_trigger_bite(void); +int qcom_wdt_suspend(struct device *dev); +int qcom_wdt_resume(struct device *dev); +int qcom_wdt_register(struct platform_device *pdev, + struct msm_watchdog_data *wdog_dd, + char *wdog_dd_name); +int qcom_wdt_remove(struct platform_device *pdev); +#else +static inline void qcom_wdt_trigger_bite(void) { } #endif #endif diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c index 1cb23cae1b91..4f002cd1c7d8 100644 --- a/kernel/locking/spinlock_debug.c +++ b/kernel/locking/spinlock_debug.c @@ -68,7 +68,7 @@ static void spin_dump(raw_spinlock_t *lock, const char *msg) READ_ONCE(lock->owner_cpu)); #ifdef CONFIG_DEBUG_SPINLOCK_BITE_ON_BUG - msm_trigger_wdog_bite(); + qcom_wdt_trigger_bite(); #elif defined(CONFIG_DEBUG_SPINLOCK_PANIC_ON_BUG) BUG(); #endif From 04085c1d1116dd94493170858888c007c6f0ff8e Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Wed, 29 Jul 2020 18:42:01 -0700 Subject: [PATCH 09/12] soc: qcom: Add qcom watchdog framework support Refactor the soc watchdog driver to use the qcom watchdog framework. Change-Id: I12a6afff4299bb39cacca27bcf483e08a503add0 Signed-off-by: Branden Bonaby --- drivers/soc/qcom/Kconfig | 10 +++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qcom_soc_wdt.c | 150 ++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 drivers/soc/qcom/qcom_soc_wdt.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index d69c1182e2ec..e86ddfa3bcbe 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -795,6 +795,16 @@ config QCOM_WDT_CORE watchdog module must be loaded along with this for watchdog functionality. +config QCOM_SOC_WATCHDOG + tristate "Qualcomm Technologies, Inc. Soc Watchdog" + depends on QCOM_WDT_CORE + help + This enables the Qualcomm Technologies, Inc. watchdog module for the + Soc. It provides an interface to perform watchdog actions such as + setting the bark/bite time and also petting the hardware watchdog. To + utilize this the Qualcomm Technologies, Inc. watchdog framework must + also be enabled. + config QCOM_FORCE_WDOG_BITE_ON_PANIC bool "QCOM force watchdog bite on panic" depends on QCOM_WDT_CORE diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index a18a6cfc768e..e10f95182d22 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_QTI_CRYPTO_COMMON) += crypto-qti-common.o obj-$(CONFIG_QTI_CRYPTO_TZ) += crypto-qti-tz.o obj-$(CONFIG_QTI_HW_KEY_MANAGER) += hwkm.o crypto-qti-hwkm.o obj-$(CONFIG_QCOM_WDT_CORE) += qcom_wdt_core.o +obj-$(CONFIG_QCOM_SOC_WATCHDOG) += qcom_soc_wdt.o ifdef CONFIG_DEBUG_FS obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o endif diff --git a/drivers/soc/qcom/qcom_soc_wdt.c b/drivers/soc/qcom/qcom_soc_wdt.c new file mode 100644 index 000000000000..302751c1725e --- /dev/null +++ b/drivers/soc/qcom/qcom_soc_wdt.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#define WDT0_RST 0x04 +#define WDT0_EN 0x08 +#define WDT0_STS 0x0C +#define WDT0_BARK_TIME 0x10 +#define WDT0_BITE_TIME 0x14 +#define WDT_HZ 32765 + +static inline int qcom_soc_set_wdt_bark(u32 time, + struct msm_watchdog_data *wdog_dd) +{ + __raw_writel((time * WDT_HZ)/1000, wdog_dd->base + WDT0_BARK_TIME); + /* Make sure register write is complete before proceeding */ + mb(); + return 0; +} + +static inline int qcom_soc_set_wdt_bite(u32 time, + struct msm_watchdog_data *wdog_dd) +{ + __raw_writel((time * WDT_HZ)/1000, wdog_dd->base + WDT0_BITE_TIME); + /* Make sure register write is complete before proceeding */ + mb(); + return 0; +} + + + +static inline int qcom_soc_reset_wdt(struct msm_watchdog_data *wdog_dd) +{ + __raw_writel(1, wdog_dd->base + WDT0_RST); + /* Make sure register write is complete before proceeding */ + mb(); + return 0; +} + +static inline int qcom_soc_enable_wdt(u32 val, + struct msm_watchdog_data *wdog_dd) +{ + __raw_writel(val, wdog_dd->base + WDT0_EN); + /* Make sure register write is complete before proceeding */ + mb(); + return 0; +} + +static inline int qcom_soc_disable_wdt(struct msm_watchdog_data *wdog_dd) +{ + __raw_writel(0, wdog_dd->base + WDT0_EN); + /* Make sure register write is complete before proceeding */ + mb(); + return 0; +} + +static inline int qcom_soc_show_wdt_status(struct msm_watchdog_data *wdog_dd) +{ + dev_err(wdog_dd->dev, "Wdog - STS: 0x%x, CTL: 0x%x, BARK TIME: 0x%x, BITE TIME: 0x%x\n", + __raw_readl(wdog_dd->base + WDT0_STS), + __raw_readl(wdog_dd->base + WDT0_EN), + __raw_readl(wdog_dd->base + WDT0_BARK_TIME), + __raw_readl(wdog_dd->base + WDT0_BITE_TIME)); + return 0; +} + +static struct qcom_wdt_ops qcom_soc_wdt_ops = { + .set_bark_time = qcom_soc_set_wdt_bark, + .set_bite_time = qcom_soc_set_wdt_bite, + .reset_wdt = qcom_soc_reset_wdt, + .enable_wdt = qcom_soc_enable_wdt, + .disable_wdt = qcom_soc_disable_wdt, + .show_wdt_status = qcom_soc_show_wdt_status +}; + +static int qcom_soc_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + struct msm_watchdog_data *wdog_dd; + + wdog_dd = devm_kzalloc(&pdev->dev, sizeof(*wdog_dd), GFP_KERNEL); + if (!wdog_dd) + return -ENOMEM; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wdt-base"); + if (!res) + return -ENODEV; + + wdog_dd->base = devm_ioremap_resource(&pdev->dev, res); + if (!wdog_dd->base) { + dev_err(&pdev->dev, "%s cannot map wdog register space\n", + __func__); + return -ENXIO; + } + wdog_dd->ops = &qcom_soc_wdt_ops; + + return qcom_wdt_register(pdev, wdog_dd, "msm-watchdog"); +} + +#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops qcom_soc_dev_pm_ops = { + .suspend_noirq = qcom_wdt_suspend, + .resume_noirq = qcom_wdt_resume, +}; +#else +static const struct dev_pm_ops qcom_soc_dev_pm_ops = { +}; +#endif + +static const struct of_device_id qcom_soc_match_table[] = { + { .compatible = "qcom,msm-watchdog" }, + {} +}; + +static struct platform_driver qcom_soc_wdt_driver = { + .probe = qcom_soc_wdt_probe, + .remove = qcom_wdt_remove, + .driver = { + .name = "msm_watchdog", + .pm = &qcom_soc_dev_pm_ops, + .of_match_table = qcom_soc_match_table, + }, +}; + +static int __init init_watchdog(void) +{ + return platform_driver_register(&qcom_soc_wdt_driver); +} + +#if IS_MODULE(CONFIG_QCOM_SOC_WATCHDOG) +module_init(init_watchdog); +#else +pure_initcall(init_watchdog); +#endif + +static __exit void exit_watchdog(void) +{ + platform_driver_unregister(&qcom_soc_wdt_driver); +} +module_exit(exit_watchdog); +MODULE_DESCRIPTION("QCOM Soc Watchdog Driver"); +MODULE_LICENSE("GPL v2"); From 86d73e7f8c0f105bfd9cffb7535aec8bf4a9b8c6 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Thu, 2 Jul 2020 08:33:08 -0700 Subject: [PATCH 10/12] haven: watchdog: Add virtual watchdog support This driver takes advantage of the watchdog api in the hypervisor through the use of ARM SMCCC calls. It allows for communication between the watchdog code in the kernel, and the watchdog api in the hypervisor. Change-Id: I9406f066fb9fd1b808a4b52a328b0c6c8185238e Signed-off-by: Branden Bonaby --- drivers/virt/haven/Kconfig | 9 + drivers/virt/haven/Makefile | 1 + drivers/virt/haven/hh_virt_wdt.c | 306 +++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 drivers/virt/haven/hh_virt_wdt.c diff --git a/drivers/virt/haven/Kconfig b/drivers/virt/haven/Kconfig index 86331d4cba33..fdb017f5995f 100644 --- a/drivers/virt/haven/Kconfig +++ b/drivers/virt/haven/Kconfig @@ -90,4 +90,13 @@ config HH_MEM_NOTIFIER machines, and register callbacks that get invoked only when notifications pertaining to their memory resources arrive. +config HH_VIRT_WATCHDOG + tristate "Haven Virtual Watchdog Driver" + depends on QCOM_WDT_CORE + help + This enables the Qualcomm Technologies, Inc. watchdog module for + the Haven hypervisor. It provides an interface to perform watchdog + actions such as setting the bark/bite time and also petting the + watchdog in the hypervisor. + endif diff --git a/drivers/virt/haven/Makefile b/drivers/virt/haven/Makefile index f43f41bda2e8..a57eba1a420b 100644 --- a/drivers/virt/haven/Makefile +++ b/drivers/virt/haven/Makefile @@ -6,3 +6,4 @@ hh_rm_drv-y := hh_rm_core.o hh_rm_iface.o obj-$(CONFIG_HH_DBL) += hh_dbl.o obj-$(CONFIG_HH_IRQ_LEND) += hh_irq_lend.o obj-$(CONFIG_HH_MEM_NOTIFIER) += hh_mem_notifier.o +obj-$(CONFIG_HH_VIRT_WATCHDOG)+= hh_virt_wdt.o \ No newline at end of file diff --git a/drivers/virt/haven/hh_virt_wdt.c b/drivers/virt/haven/hh_virt_wdt.c new file mode 100644 index 000000000000..442f9e5d1f0b --- /dev/null +++ b/drivers/virt/haven/hh_virt_wdt.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#define VIRT_WDT_CONTROL \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\ + ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0x0005) +#define VIRT_WDT_STATUS \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\ + ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0x0006) +#define VIRT_WDT_PET \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\ + ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0x0007) +#define VIRT_WDT_SET_TIME \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\ + ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0x0008) +#define VIRT_WDT_NO_CHANGE 0xFFFF + +/** + * hh_wdt_call() - Sends ARM SMCCC 1.1 Calls to the hypervisor + * + * @smc_id: The smc id needed to interact with the watchdog in the hypervisor + * @arg1: A u32 value to be sent to the the hypervisor + * @arg2: A u16 value to be sent to the the hypervisor + * @arg3: A u16 value to be sent to the the hypervisor + * + * The hypervisor takes input via ARM SMCCC Calls. The position of + * these values matter. u16 values are needed to set both the bark + * and bite time. In all other cases only the u32 (arg1) value is required. + * + * return: 0 on success, negative errno on failure. + */ +static struct arm_smccc_res hh_wdt_call(u32 smc_id, u32 arg1, + u16 arg2, u16 arg3) +{ + struct arm_smccc_res res; + + if (smc_id == VIRT_WDT_SET_TIME) + /* virtual watchdog expecting u16 values for bark and bite */ + arm_smccc_1_1_smc(smc_id, arg2, arg3, &res); + else + arm_smccc_1_1_smc(smc_id, arg1, &res); + + return res; +} + +/** + * hh_set_wdt_bark() - Sets the bark time for the virtual watchdog + * + * @time: A u32 value to be converted to milliseconds (u16) + * @wdog_dd: The qcom watchdog data structure + * + * The hypervisor requires both the bark and the bite time in the same + * call. To update one and not the other, the value VIRT_WDT_NO_CHANGE + * is used. + * + * return: 0 on success, negative errno on failure. + */ +static int hh_set_wdt_bark(u32 time, struct msm_watchdog_data *wdog_dd) +{ + struct arm_smccc_res res; + int hret, ret; + u16 bark_time; + + bark_time = (u16) time; + res = hh_wdt_call(VIRT_WDT_SET_TIME, 0, bark_time, VIRT_WDT_NO_CHANGE); + hret = res.a0; + ret = hh_remap_error(hret); + if (hret) { + dev_err(wdog_dd->dev, "failed to set bark time for vDOG, hret = %d ret = %d\n", + hret, ret); + } + + return ret; +} + +/** + * hh_set_wdt_bite() - Sets the bite time for the virtual watchdog + * + * @time: A u32 value to be converted to milliseconds (u16) + * @wdog_dd: The qcom watchdog data structure + * + * The hypervisor requires both the bark and the bite time in the same + * call. To update one and not the other, the value VIRT_WDT_NO_CHANGE + * is used. + * + * return: 0 on success, negative errno on failure. + */ +static int hh_set_wdt_bite(u32 time, struct msm_watchdog_data *wdog_dd) +{ + struct arm_smccc_res res; + int hret, ret; + u16 bite_time; + + bite_time = (u16) time; + res = hh_wdt_call(VIRT_WDT_SET_TIME, 0, VIRT_WDT_NO_CHANGE, bite_time); + hret = res.a0; + ret = hh_remap_error(hret); + if (hret) { + dev_err(wdog_dd->dev, "failed to set bite time for vWDOG, hret = %d ret = %d\n", + hret, ret); + } + + return ret; +} + +/** + * hh_reset_wdt() - Resets the virtual watchdog timer + * + * @wdog_dd: The qcom watchdog data structure + * + * VIRT_WDT_PET is used to reset the virtual watchdog. + * + * return: 0 on success, negative errno on failure. + */ +static int hh_reset_wdt(struct msm_watchdog_data *wdog_dd) +{ + struct arm_smccc_res res; + int hret, ret; + + res = hh_wdt_call(VIRT_WDT_PET, 0, 0, 0); + hret = res.a0; + ret = hh_remap_error(hret); + if (hret) { + dev_err(wdog_dd->dev, "failed to reset vWDOG, hret = %d ret = %d\n", + hret, ret); + } + + return ret; +} + +/** + * hh_enable_wdt() - Enables the virtual watchdog + * + * @wdog_dd: The qcom watchdog data structure + * @state: state value to send to watchdog + * + * VIRT_WDT_CONTROL is used to enable the virtual watchdog. + * Bit 0 is used to enable the watchdog. When this Bit is set to + * 1 the watchdog is enabled. NOTE: Bit 1 must always be set to + * 1 as this bit is reserved in the hypervisor and Bit 1 is + * expected to be 1. If this Bit is not set, the hypervisor will + * return an error. So to enable the watchdog you must use the value 3. + * An error from the hypervisor is expected if you try to enable the + * watchdog when its already enabled. + * + * return: 0 on success, negative errno on failure. + */ +static int hh_enable_wdt(u32 state, struct msm_watchdog_data *wdog_dd) +{ + struct arm_smccc_res res; + int hret, ret; + + if (wdog_dd->enabled) { + dev_err(wdog_dd->dev, "vWDT already enabled\n"); + return 0; + } + res = hh_wdt_call(VIRT_WDT_CONTROL, 3, 0, 0); + hret = res.a0; + ret = hh_remap_error(hret); + if (hret) { + dev_err(wdog_dd->dev, "failed enabling vWDOG, hret = %d ret = %d\n", + hret, ret); + } + + return ret; +} + +/** + * hh_disable_wdt() - Disables the virtual watchdog + * + * @wdog_dd: The qcom watchdog data structure + * + * VIRT_WDT_CONTROL is used to disable the virtual watchdog. + * Bit 0 is used to disable the watchdog. When this Bit is set to + * 0 the watchdog is disabled. NOTE: Bit 1 must always be set to + * 1 as this bit is reserved in the hypervisor and Bit 1 is + * expected to be 1. If this Bit is not set, the hypervisor will + * return an error. So to disable the watchdog you must use the value 2. + * An error from the hypervisor is expected if you try to disable the + * watchdog when its already disabled. + * + * return: 0 on success, negative errno on failure. + */ +static int hh_disable_wdt(struct msm_watchdog_data *wdog_dd) +{ + struct arm_smccc_res res; + int hret, ret; + + if (!wdog_dd->enabled) { + dev_err(wdog_dd->dev, "vWDT already disabled\n"); + return 0; + } + res = hh_wdt_call(VIRT_WDT_CONTROL, 2, 0, 0); + hret = res.a0; + ret = hh_remap_error(hret); + if (hret) { + dev_err(wdog_dd->dev, "failed disabling VDOG, hret = %d ret = %d\n", + hret, ret); + } + + return ret; +} + +/** + * hh_get_wdt_status() - Displays the status of the virtual watchdog + * + * @wdog_dd: The qcom watchdog data structure + * + * VIRT_WDT_STATUS is used to display status of the virtual watchdog. + * + * return: 0 on success, negative errno on failure. + */ +static int hh_show_wdt_status(struct msm_watchdog_data *wdog_dd) +{ + struct arm_smccc_res res; + int hret, ret; + + res = hh_wdt_call(VIRT_WDT_STATUS, 0, 0, 0); + hret = res.a0; + ret = hh_remap_error(hret); + if (hret) { + dev_err(wdog_dd->dev, "failed to get vWDOG status, hret = %d ret = %d\n", + hret, ret); + } else { + dev_err(wdog_dd->dev, + "vWdog-CTL: %d, vWdog-time since last pet: %d, vWdog-expired status: %d\n", + res.a1 & 1, res.a2, (res.a1 >> 31) & 1); + } + + return ret; +} + +static struct qcom_wdt_ops hh_wdt_ops = { + .set_bark_time = hh_set_wdt_bark, + .set_bite_time = hh_set_wdt_bite, + .reset_wdt = hh_reset_wdt, + .enable_wdt = hh_enable_wdt, + .disable_wdt = hh_disable_wdt, + .show_wdt_status = hh_show_wdt_status +}; + +static int hh_wdt_probe(struct platform_device *pdev) +{ + struct msm_watchdog_data *wdog_dd; + + wdog_dd = devm_kzalloc(&pdev->dev, sizeof(*wdog_dd), GFP_KERNEL); + if (!wdog_dd) + return -ENOMEM; + wdog_dd->ops = &hh_wdt_ops; + + return qcom_wdt_register(pdev, wdog_dd, "hh-watchdog"); +} + +#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops hh_wdt_dev_pm_ops = { + .suspend_noirq = qcom_wdt_suspend, + .resume_noirq = qcom_wdt_resume, +}; +#else +static const struct dev_pm_ops hh_wdt_dev_pm_ops = { +}; +#endif + +static const struct of_device_id hh_wdt_match_table[] = { + { .compatible = "qcom,hh-watchdog" }, + {} +}; + +static struct platform_driver hh_wdt_driver = { + .probe = hh_wdt_probe, + .remove = qcom_wdt_remove, + .driver = { + .name = "hh-watchdog", + .pm = &hh_wdt_dev_pm_ops, + .of_match_table = hh_wdt_match_table, + }, +}; + +static int __init init_watchdog(void) +{ + return platform_driver_register(&hh_wdt_driver); +} + +#if IS_MODULE(CONFIG_HH_VIRT_WATCHDOG) +module_init(init_watchdog); +#else +pure_initcall(init_watchdog); +#endif + +static __exit void exit_watchdog(void) +{ + platform_driver_unregister(&hh_wdt_driver); +} +module_exit(exit_watchdog); +MODULE_DESCRIPTION("QCOM Haven Watchdog Driver"); +MODULE_LICENSE("GPL v2"); From 3c963c75927ed2ad002e05da67af03a8b6a3bf8d Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Tue, 28 Jul 2020 20:42:16 -0700 Subject: [PATCH 11/12] arm64: defconfig: Enabled SOC and Haven watchdog drivers Add SOC and Haven watchdog drivers for Lahaina. Change-Id: Ia9283c9e7ee8cb99377bc3460f58c3433d28092a Signed-off-by: Branden Bonaby --- arch/arm64/configs/vendor/lahaina_GKI.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/vendor/lahaina_GKI.config b/arch/arm64/configs/vendor/lahaina_GKI.config index 687052e2abd6..40ae62d56949 100644 --- a/arch/arm64/configs/vendor/lahaina_GKI.config +++ b/arch/arm64/configs/vendor/lahaina_GKI.config @@ -232,3 +232,5 @@ CONFIG_SM_DISPCC_SHIMA=m CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_INCREMENTAL_FS=m CONFIG_QCOM_MICRODUMP=m +CONFIG_HH_VIRT_WATCHDOG=m +CONFIG_QCOM_SOC_WATCHDOG=m From f2f127dcdffae9045d347d5b5344feeecb729ab4 Mon Sep 17 00:00:00 2001 From: Branden Bonaby Date: Tue, 28 Jul 2020 17:03:40 -0700 Subject: [PATCH 12/12] arm64: defconfig: Set panic timeout and panic oops config To allow the Secure VM to function similarly to the primary VM during a panic, the panic timeout and panic on oops configs need to be set the same way they are set in the generic GKI configs. Currently this is not set so the default is 0, meaning that during a panic the watchdog becomes disabled and the system does not reboot. Change-Id: Ib92df399da28cd8a39ad7d972fcc56fa2ad0f46a Signed-off-by: Branden Bonaby --- arch/arm64/configs/vendor/genericarmv8-64_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/vendor/genericarmv8-64_defconfig b/arch/arm64/configs/vendor/genericarmv8-64_defconfig index 3f9a3e5f7f43..4d0340a5c8a9 100644 --- a/arch/arm64/configs/vendor/genericarmv8-64_defconfig +++ b/arch/arm64/configs/vendor/genericarmv8-64_defconfig @@ -345,6 +345,8 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_FS=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_TIMEOUT=-1 # CONFIG_SCHED_DEBUG is not set # CONFIG_DEBUG_PREEMPT is not set CONFIG_STACKTRACE=y