xref: /optee_os/core/drivers/sp805_wdt.c (revision 5f7f88c6b9d618d1e068166bbf2b07757350791d)
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 
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 
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 
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 
66 static void sp805_ping(struct wdt_chip *chip)
67 {
68 	sp805_config(chip, false);
69 }
70 
71 static void sp805_enable(struct wdt_chip *chip)
72 {
73 	sp805_config(chip, true);
74 }
75 
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 
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 
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 	TEE_Result res = TEE_ERROR_GENERIC;
106 	struct itr_handler *wdt_itr = NULL;
107 
108 	assert(!pd->chip.wdt_itr);
109 
110 	res = interrupt_alloc_add_handler(interrupt_get_main_chip(), itr_num,
111 					  wdt_itr_cb, itr_flags,
112 					  &pd->chip, &wdt_itr);
113 	if (res)
114 		return res;
115 
116 	pd->itr_handler = itr_handler;
117 	pd->chip.wdt_itr = wdt_itr;
118 
119 	interrupt_enable(wdt_itr->chip, wdt_itr->it);
120 
121 	return TEE_SUCCESS;
122 }
123 
124 static const struct wdt_ops sp805_wdt_ops = {
125 	.start = sp805_enable,
126 	.stop = sp805_disable,
127 	.ping = sp805_ping,
128 	.set_timeout = sp805_setload,
129 };
130 DECLARE_KEEP_PAGER(sp805_wdt_ops);
131 
132 TEE_Result sp805_wdt_init(struct sp805_wdt_data *pd, paddr_t base,
133 		    uint32_t clk_rate, uint32_t timeout)
134 {
135 	assert(pd);
136 	pd->base.pa = base;
137 	pd->clk_rate = clk_rate;
138 	pd->chip.ops = &sp805_wdt_ops;
139 	return sp805_setload(&pd->chip, timeout);
140 }
141