1cbb41c91SAbhishek Shah // SPDX-License-Identifier: BSD-2-Clause
2cbb41c91SAbhishek Shah /*
3cbb41c91SAbhishek Shah * Copyright (c) 2019, Broadcom
4cbb41c91SAbhishek Shah */
5cbb41c91SAbhishek Shah
6cbb41c91SAbhishek Shah #include <drivers/sp805_wdt.h>
7cbb41c91SAbhishek Shah #include <initcall.h>
8cbb41c91SAbhishek Shah #include <io.h>
9cbb41c91SAbhishek Shah #include <keep.h>
10cbb41c91SAbhishek Shah #include <kernel/interrupt.h>
11cbb41c91SAbhishek Shah #include <mm/core_memprot.h>
12cbb41c91SAbhishek Shah #include <stdint.h>
13cbb41c91SAbhishek Shah #include <trace.h>
14cbb41c91SAbhishek Shah
chip_to_base(struct wdt_chip * chip)15cbb41c91SAbhishek Shah static vaddr_t chip_to_base(struct wdt_chip *chip)
16cbb41c91SAbhishek Shah {
17cbb41c91SAbhishek Shah struct sp805_wdt_data *pd =
18cbb41c91SAbhishek Shah container_of(chip, struct sp805_wdt_data, chip);
19cbb41c91SAbhishek Shah
20c2e4eb43SAnton Rybakov return io_pa_or_va(&pd->base, WDT_SIZE);
21cbb41c91SAbhishek Shah }
22cbb41c91SAbhishek Shah
sp805_setload(struct wdt_chip * chip,unsigned long timeout)23cbb41c91SAbhishek Shah static TEE_Result sp805_setload(struct wdt_chip *chip, unsigned long timeout)
24cbb41c91SAbhishek Shah {
25cbb41c91SAbhishek Shah struct sp805_wdt_data *pd =
26cbb41c91SAbhishek Shah container_of(chip, struct sp805_wdt_data, chip);
27cbb41c91SAbhishek Shah uint32_t load = 0;
28cbb41c91SAbhishek Shah
29cbb41c91SAbhishek Shah /*
30cbb41c91SAbhishek Shah * sp805 runs counter with given value twice, after the end of first
31cbb41c91SAbhishek Shah * counter it gives an interrupt and then starts counter again. If
32cbb41c91SAbhishek Shah * interrupt already occurred then it resets the system. This is why
33cbb41c91SAbhishek Shah * load is half of what should be required.
34cbb41c91SAbhishek Shah */
35cbb41c91SAbhishek Shah if (MUL_OVERFLOW(timeout, pd->clk_rate, &load))
36cbb41c91SAbhishek Shah return TEE_ERROR_SECURITY;
37cbb41c91SAbhishek Shah
38cbb41c91SAbhishek Shah load = (load / 2) - 1;
39cbb41c91SAbhishek Shah if (load < WDT_LOAD_MIN)
40cbb41c91SAbhishek Shah load = WDT_LOAD_MIN;
41cbb41c91SAbhishek Shah
42cbb41c91SAbhishek Shah pd->load_val = load;
43cbb41c91SAbhishek Shah return TEE_SUCCESS;
44cbb41c91SAbhishek Shah }
45cbb41c91SAbhishek Shah
sp805_config(struct wdt_chip * chip,bool enable)46cbb41c91SAbhishek Shah static void sp805_config(struct wdt_chip *chip, bool enable)
47cbb41c91SAbhishek Shah {
48cbb41c91SAbhishek Shah struct sp805_wdt_data *pd =
49cbb41c91SAbhishek Shah container_of(chip, struct sp805_wdt_data, chip);
50cbb41c91SAbhishek Shah vaddr_t base = chip_to_base(chip);
51cbb41c91SAbhishek Shah
52cbb41c91SAbhishek Shah io_write32(base + WDT_LOCK_OFFSET, WDT_UNLOCK_KEY);
53cbb41c91SAbhishek Shah io_write32(base + WDT_LOAD_OFFSET, pd->load_val);
54cbb41c91SAbhishek Shah io_write32(base + WDT_INTCLR_OFFSET, WDT_INT_CLR);
55cbb41c91SAbhishek Shah
56cbb41c91SAbhishek Shah if (enable)
57cbb41c91SAbhishek Shah io_write32(base + WDT_CONTROL_OFFSET,
58cbb41c91SAbhishek Shah WDT_INT_EN | WDT_RESET_EN);
59cbb41c91SAbhishek Shah
60cbb41c91SAbhishek Shah io_write32(base + WDT_LOCK_OFFSET, WDT_LOCK_KEY);
61cbb41c91SAbhishek Shah
62cbb41c91SAbhishek Shah /* Flush posted writes. */
63cbb41c91SAbhishek Shah (void)io_read32(base + WDT_LOCK_OFFSET);
64cbb41c91SAbhishek Shah }
65cbb41c91SAbhishek Shah
sp805_ping(struct wdt_chip * chip)66cbb41c91SAbhishek Shah static void sp805_ping(struct wdt_chip *chip)
67cbb41c91SAbhishek Shah {
68cbb41c91SAbhishek Shah sp805_config(chip, false);
69cbb41c91SAbhishek Shah }
70cbb41c91SAbhishek Shah
sp805_enable(struct wdt_chip * chip)71cbb41c91SAbhishek Shah static void sp805_enable(struct wdt_chip *chip)
72cbb41c91SAbhishek Shah {
73cbb41c91SAbhishek Shah sp805_config(chip, true);
74cbb41c91SAbhishek Shah }
75cbb41c91SAbhishek Shah
sp805_disable(struct wdt_chip * chip)76cbb41c91SAbhishek Shah static void sp805_disable(struct wdt_chip *chip)
77cbb41c91SAbhishek Shah {
78cbb41c91SAbhishek Shah vaddr_t base = chip_to_base(chip);
79cbb41c91SAbhishek Shah
80cbb41c91SAbhishek Shah io_write32(base + WDT_LOCK_OFFSET, WDT_UNLOCK_KEY);
81cbb41c91SAbhishek Shah io_write32(base + WDT_CONTROL_OFFSET, 0);
82cbb41c91SAbhishek Shah io_write32(base + WDT_LOCK_OFFSET, WDT_LOCK_KEY);
83cbb41c91SAbhishek Shah
84cbb41c91SAbhishek Shah /* Flush posted writes. */
85cbb41c91SAbhishek Shah (void)io_read32(base + WDT_LOCK_OFFSET);
86cbb41c91SAbhishek Shah }
87cbb41c91SAbhishek Shah
wdt_itr_cb(struct itr_handler * h)88cbb41c91SAbhishek Shah static enum itr_return wdt_itr_cb(struct itr_handler *h)
89cbb41c91SAbhishek Shah {
90cbb41c91SAbhishek Shah struct wdt_chip *chip = h->data;
91cbb41c91SAbhishek Shah struct sp805_wdt_data *pd =
92cbb41c91SAbhishek Shah container_of(chip, struct sp805_wdt_data, chip);
93cbb41c91SAbhishek Shah
94cbb41c91SAbhishek Shah if (pd->itr_handler)
95cbb41c91SAbhishek Shah pd->itr_handler(chip);
96cbb41c91SAbhishek Shah
97cbb41c91SAbhishek Shah return ITRR_HANDLED;
98cbb41c91SAbhishek Shah }
993639b55fSJerome Forissier DECLARE_KEEP_PAGER(wdt_itr_cb);
100cbb41c91SAbhishek Shah
sp805_register_itr_handler(struct sp805_wdt_data * pd,uint32_t itr_num,uint32_t itr_flags,sp805_itr_handler_func_t itr_handler)101cbb41c91SAbhishek Shah TEE_Result sp805_register_itr_handler(struct sp805_wdt_data *pd,
102cbb41c91SAbhishek Shah uint32_t itr_num, uint32_t itr_flags,
103cbb41c91SAbhishek Shah sp805_itr_handler_func_t itr_handler)
104cbb41c91SAbhishek Shah {
105*eb5b947dSEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC;
106*eb5b947dSEtienne Carriere struct itr_handler *wdt_itr = NULL;
107cbb41c91SAbhishek Shah
108cbb41c91SAbhishek Shah assert(!pd->chip.wdt_itr);
109cbb41c91SAbhishek Shah
110*eb5b947dSEtienne Carriere res = interrupt_alloc_add_handler(interrupt_get_main_chip(), itr_num,
111*eb5b947dSEtienne Carriere wdt_itr_cb, itr_flags,
112*eb5b947dSEtienne Carriere &pd->chip, &wdt_itr);
113*eb5b947dSEtienne Carriere if (res)
114*eb5b947dSEtienne Carriere return res;
115cbb41c91SAbhishek Shah
116cbb41c91SAbhishek Shah pd->itr_handler = itr_handler;
117cbb41c91SAbhishek Shah pd->chip.wdt_itr = wdt_itr;
118cbb41c91SAbhishek Shah
119*eb5b947dSEtienne Carriere interrupt_enable(wdt_itr->chip, wdt_itr->it);
120cbb41c91SAbhishek Shah
121cbb41c91SAbhishek Shah return TEE_SUCCESS;
122cbb41c91SAbhishek Shah }
123cbb41c91SAbhishek Shah
124cbb41c91SAbhishek Shah static const struct wdt_ops sp805_wdt_ops = {
125cbb41c91SAbhishek Shah .start = sp805_enable,
126cbb41c91SAbhishek Shah .stop = sp805_disable,
127cbb41c91SAbhishek Shah .ping = sp805_ping,
128cbb41c91SAbhishek Shah .set_timeout = sp805_setload,
129cbb41c91SAbhishek Shah };
1303639b55fSJerome Forissier DECLARE_KEEP_PAGER(sp805_wdt_ops);
131cbb41c91SAbhishek Shah
sp805_wdt_init(struct sp805_wdt_data * pd,paddr_t base,uint32_t clk_rate,uint32_t timeout)132cbb41c91SAbhishek Shah TEE_Result sp805_wdt_init(struct sp805_wdt_data *pd, paddr_t base,
133cbb41c91SAbhishek Shah uint32_t clk_rate, uint32_t timeout)
134cbb41c91SAbhishek Shah {
135cbb41c91SAbhishek Shah assert(pd);
136cbb41c91SAbhishek Shah pd->base.pa = base;
137cbb41c91SAbhishek Shah pd->clk_rate = clk_rate;
138cbb41c91SAbhishek Shah pd->chip.ops = &sp805_wdt_ops;
139cbb41c91SAbhishek Shah return sp805_setload(&pd->chip, timeout);
140cbb41c91SAbhishek Shah }
141