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