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