aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbhishek Shah <abhishek.shah@broadcom.com>2019-05-30 16:01:03 +0530
committerJerome Forissier <jerome.forissier@linaro.org>2019-08-13 09:43:56 +0200
commitcbb41c9147d0c4c2a2a31f80425a8df07835f0a1 (patch)
tree9d1f6c6c3b8c9fc2c8801298922606184a5aa09f
parentf5df167c2ffb2b26de30bebe6e7a0674924b4de7 (diff)
drivers: wdt: Add arm SP805 watchdog driver
Add sp805 watchdog driver with following functionality: - start/reload watchdog with specified timeout - stop watchdog - ping watchdog (clear watchdog interrupt and reload it) - register watchdog interrupt handler Signed-off-by: Abhishek Shah <abhishek.shah@broadcom.com> Reviewed-by: Sandeep Tripathy <sandeep.tripathy@broadcom.com> Acked-by: Jens Wiklander <jens.wiklander@linaro.org> Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
-rw-r--r--core/arch/arm/plat-bcm/conf.mk1
-rw-r--r--core/drivers/sp805_wdt.c142
-rw-r--r--core/drivers/sub.mk1
-rw-r--r--core/include/drivers/sp805_wdt.h68
-rw-r--r--core/include/drivers/wdt.h36
5 files changed, 248 insertions, 0 deletions
diff --git a/core/arch/arm/plat-bcm/conf.mk b/core/arch/arm/plat-bcm/conf.mk
index b6d2ca5d..73d9d545 100644
--- a/core/arch/arm/plat-bcm/conf.mk
+++ b/core/arch/arm/plat-bcm/conf.mk
@@ -23,6 +23,7 @@ $(call force,CFG_SECURE_TIME_SOURCE_CNTPCT,y)
ifeq ($(PLATFORM_FLAVOR),ns3)
$(call force,CFG_PL022,y)
+$(call force,CFG_SP805_WDT,y)
$(call force,CFG_BCM_HWRNG,y)
$(call force,CFG_BCM_SOTP,y)
$(call force,CFG_BCM_GPIO,y)
diff --git a/core/drivers/sp805_wdt.c b/core/drivers/sp805_wdt.c
new file mode 100644
index 00000000..1d034c21
--- /dev/null
+++ b/core/drivers/sp805_wdt.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2019, Broadcom
+ */
+
+#include <drivers/sp805_wdt.h>
+#include <initcall.h>
+#include <io.h>
+#include <keep.h>
+#include <kernel/interrupt.h>
+#include <mm/core_memprot.h>
+#include <stdint.h>
+#include <trace.h>
+
+static vaddr_t chip_to_base(struct wdt_chip *chip)
+{
+ struct sp805_wdt_data *pd =
+ container_of(chip, struct sp805_wdt_data, chip);
+
+ return io_pa_or_va(&pd->base);
+}
+
+static TEE_Result sp805_setload(struct wdt_chip *chip, unsigned long timeout)
+{
+ struct sp805_wdt_data *pd =
+ container_of(chip, struct sp805_wdt_data, chip);
+ uint32_t load = 0;
+
+ /*
+ * sp805 runs counter with given value twice, after the end of first
+ * counter it gives an interrupt and then starts counter again. If
+ * interrupt already occurred then it resets the system. This is why
+ * load is half of what should be required.
+ */
+ if (MUL_OVERFLOW(timeout, pd->clk_rate, &load))
+ return TEE_ERROR_SECURITY;
+
+ load = (load / 2) - 1;
+ if (load < WDT_LOAD_MIN)
+ load = WDT_LOAD_MIN;
+
+ pd->load_val = load;
+ return TEE_SUCCESS;
+}
+
+static void sp805_config(struct wdt_chip *chip, bool enable)
+{
+ struct sp805_wdt_data *pd =
+ container_of(chip, struct sp805_wdt_data, chip);
+ vaddr_t base = chip_to_base(chip);
+
+ io_write32(base + WDT_LOCK_OFFSET, WDT_UNLOCK_KEY);
+ io_write32(base + WDT_LOAD_OFFSET, pd->load_val);
+ io_write32(base + WDT_INTCLR_OFFSET, WDT_INT_CLR);
+
+ if (enable)
+ io_write32(base + WDT_CONTROL_OFFSET,
+ WDT_INT_EN | WDT_RESET_EN);
+
+ io_write32(base + WDT_LOCK_OFFSET, WDT_LOCK_KEY);
+
+ /* Flush posted writes. */
+ (void)io_read32(base + WDT_LOCK_OFFSET);
+}
+
+static void sp805_ping(struct wdt_chip *chip)
+{
+ sp805_config(chip, false);
+}
+
+static void sp805_enable(struct wdt_chip *chip)
+{
+ sp805_config(chip, true);
+}
+
+static void sp805_disable(struct wdt_chip *chip)
+{
+ vaddr_t base = chip_to_base(chip);
+
+ io_write32(base + WDT_LOCK_OFFSET, WDT_UNLOCK_KEY);
+ io_write32(base + WDT_CONTROL_OFFSET, 0);
+ io_write32(base + WDT_LOCK_OFFSET, WDT_LOCK_KEY);
+
+ /* Flush posted writes. */
+ (void)io_read32(base + WDT_LOCK_OFFSET);
+}
+
+static enum itr_return wdt_itr_cb(struct itr_handler *h)
+{
+ struct wdt_chip *chip = h->data;
+ struct sp805_wdt_data *pd =
+ container_of(chip, struct sp805_wdt_data, chip);
+
+ if (pd->itr_handler)
+ pd->itr_handler(chip);
+
+ return ITRR_HANDLED;
+}
+KEEP_PAGER(wdt_itr_cb);
+
+TEE_Result sp805_register_itr_handler(struct sp805_wdt_data *pd,
+ uint32_t itr_num, uint32_t itr_flags,
+ sp805_itr_handler_func_t itr_handler)
+{
+ struct itr_handler *wdt_itr;
+
+ assert(!pd->chip.wdt_itr);
+
+ wdt_itr = calloc(1, sizeof(*wdt_itr));
+ if (!wdt_itr)
+ return TEE_ERROR_OUT_OF_MEMORY;
+
+ wdt_itr->it = itr_num;
+ wdt_itr->flags = itr_flags;
+ wdt_itr->handler = wdt_itr_cb;
+ wdt_itr->data = &pd->chip;
+ pd->itr_handler = itr_handler;
+ pd->chip.wdt_itr = wdt_itr;
+
+ itr_add(wdt_itr);
+ itr_enable(wdt_itr->it);
+
+ return TEE_SUCCESS;
+}
+
+static const struct wdt_ops sp805_wdt_ops = {
+ .start = sp805_enable,
+ .stop = sp805_disable,
+ .ping = sp805_ping,
+ .set_timeout = sp805_setload,
+};
+KEEP_PAGER(sp805_wdt_ops);
+
+TEE_Result sp805_wdt_init(struct sp805_wdt_data *pd, paddr_t base,
+ uint32_t clk_rate, uint32_t timeout)
+{
+ assert(pd);
+ pd->base.pa = base;
+ pd->clk_rate = clk_rate;
+ pd->chip.ops = &sp805_wdt_ops;
+ return sp805_setload(&pd->chip, timeout);
+}
diff --git a/core/drivers/sub.mk b/core/drivers/sub.mk
index 4504b5ed..22e14291 100644
--- a/core/drivers/sub.mk
+++ b/core/drivers/sub.mk
@@ -5,6 +5,7 @@ srcs-$(CFG_TZC380) += tzc380.c
srcs-$(CFG_GIC) += gic.c
srcs-$(CFG_PL061) += pl061_gpio.c
srcs-$(CFG_PL022) += pl022_spi.c
+srcs-$(CFG_SP805_WDT) += sp805_wdt.c
srcs-$(CFG_8250_UART) += serial8250_uart.c
srcs-$(CFG_16550_UART) += ns16550.c
srcs-$(CFG_IMX_SNVS) += imx_snvs.c
diff --git a/core/include/drivers/sp805_wdt.h b/core/include/drivers/sp805_wdt.h
new file mode 100644
index 00000000..2edf7d42
--- /dev/null
+++ b/core/include/drivers/sp805_wdt.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright 2019 Broadcom.
+ */
+
+#ifndef SP805_WDT_H
+#define SP805_WDT_H
+
+#include <drivers/wdt.h>
+#include <kernel/interrupt.h>
+#include <mm/core_memprot.h>
+#include <types_ext.h>
+
+/* SP805 register offset */
+#define WDT_LOAD_OFFSET 0x000
+#define WDT_CONTROL_OFFSET 0x008
+#define WDT_INTCLR_OFFSET 0x00c
+#define WDT_LOCK_OFFSET 0xc00
+
+/* Magic word to unlock the wd registers */
+#define WDT_UNLOCK_KEY 0x1ACCE551
+#define WDT_LOCK_KEY 0x1
+
+/* Register field definitions */
+#define WDT_INT_EN BIT(0)
+#define WDT_RESET_EN BIT(1)
+#define WDT_INT_CLR BIT(0)
+
+#define WDT_LOAD_MIN 0x1
+
+typedef void (*sp805_itr_handler_func_t)(struct wdt_chip *chip);
+
+struct sp805_wdt_data {
+ struct io_pa_va base;
+ struct wdt_chip chip;
+ uint32_t clk_rate;
+ uint32_t load_val;
+ uint32_t itr_num;
+ sp805_itr_handler_func_t itr_handler;
+};
+
+/*
+ * Initialize sp805 watchdog timer
+ *
+ * @pd: allocated sp805 watchdog timer platform data
+ * @base: physical base address of sp805 watchdog timer
+ * @clk_rate: rate of the clock driving the watchdog timer hardware
+ * @timeout: watchdog timer timeout in seconds
+ * Return a TEE_Result compliant status
+ */
+TEE_Result sp805_wdt_init(struct sp805_wdt_data *pd, paddr_t base,
+ uint32_t clk_rate, uint32_t timeout);
+
+/*
+ * Optionally register sp805 watchdog timer interrupt handler
+ *
+ * @pd: platform data of sp805 watchdog timer for which interrupt handler
+ * is to be registered
+ * @itr_num: sp805 watchdog timer interrupt id
+ * @itr_flag: interrupt attributes
+ * @itr_handler: Optional interrupt handler callback
+ * Return a TEE_Result compliant status
+ */
+TEE_Result sp805_register_itr_handler(struct sp805_wdt_data *pd,
+ uint32_t itr_num, uint32_t itr_flag,
+ sp805_itr_handler_func_t itr_handler);
+
+#endif /* SP805_WDT_H */
diff --git a/core/include/drivers/wdt.h b/core/include/drivers/wdt.h
new file mode 100644
index 00000000..5f736dcb
--- /dev/null
+++ b/core/include/drivers/wdt.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright 2019 Broadcom.
+ */
+
+#ifndef DRIVERS_WDT_H
+#define DRIVERS_WDT_H
+
+#include <kernel/interrupt.h>
+#include <tee_api_types.h>
+
+struct wdt_chip {
+ const struct wdt_ops *ops;
+ struct itr_handler *wdt_itr;
+};
+
+/*
+ * struct wdt_ops - The watchdog device operations
+ *
+ * @start: The routine for starting the watchdog device.
+ * @stop: The routine for stopping the watchdog device.
+ * @ping: The routine that sends a keepalive ping to the watchdog device.
+ * @set_timeout:The routine that finds the load value that will reset system in
+ * required timeout (in seconds).
+ *
+ * The wdt_ops structure contains a list of low-level operations
+ * that control a watchdog device.
+ */
+struct wdt_ops {
+ void (*start)(struct wdt_chip *chip);
+ void (*stop)(struct wdt_chip *chip);
+ void (*ping)(struct wdt_chip *chip);
+ TEE_Result (*set_timeout)(struct wdt_chip *chip, unsigned long timeout);
+};
+
+#endif /* DRIVERS_WDT_H */