1 /*
2  * Copyright (c) 2020, Google LLC. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <common/debug.h>
8 #include <lib/mmio.h>
9 #include <mt8173_def.h>
10 #include <plat_sip_calls.h>
11 #include <lib/psci/psci.h>
12 #include <smccc_helpers.h>
13 #include <wdt.h>
14 
15 #define WDT_BASE		(RGU_BASE + 0)
16 #define WDT_MODE		(WDT_BASE + 0x00)
17 #define WDT_LENGTH		(WDT_BASE + 0x04)
18 #define WDT_RESTART		(WDT_BASE + 0x08)
19 #define WDT_SWRST		(WDT_BASE + 0x14)
20 
21 #define WDT_MODE_DUAL_MODE	0x40
22 #define WDT_MODE_IRQ		0x8
23 #define WDT_MODE_KEY		0x22000000
24 #define WDT_MODE_EXTEN		0x4
25 #define WDT_MODE_EN		0x1
26 #define WDT_LENGTH_KEY		0x8
27 #define WDT_RESTART_KEY		0x1971
28 #define WDT_SWRST_KEY		0x1209
29 
30 
31 #define WDT_MIN_TIMEOUT 1
32 #define WDT_MAX_TIMEOUT 31
33 
34 enum smcwd_call {
35 	SMCWD_INFO		= 0,
36 	SMCWD_SET_TIMEOUT	= 1,
37 	SMCWD_ENABLE		= 2,
38 	SMCWD_PET		= 3,
39 };
40 
41 static int wdt_enabled_before_suspend;
42 
43 /*
44  * We expect the WDT registers to be correctly initialized by BL2 firmware
45  * (which may be board specific), so we do not reinitialize them here.
46  */
47 
wdt_trigger_reset(void)48 void wdt_trigger_reset(void)
49 {
50 	mmio_write_32(WDT_SWRST, WDT_SWRST_KEY);
51 }
52 
wdt_pet(void)53 void wdt_pet(void)
54 {
55 	mmio_write_32(WDT_RESTART, WDT_RESTART_KEY);
56 }
57 
wdt_set_timeout(uint32_t timeout)58 int wdt_set_timeout(uint32_t timeout)
59 {
60 	/* One tick here equals 512 32KHz ticks. 512 / 32000 * 125 / 2 = 1 */
61 	uint32_t ticks = timeout * 125 / 2;
62 
63 	if (timeout < WDT_MIN_TIMEOUT || timeout > WDT_MAX_TIMEOUT)
64 		return PSCI_E_INVALID_PARAMS;
65 
66 	mmio_write_32(WDT_LENGTH, ticks << 5 | WDT_LENGTH_KEY);
67 
68 	return PSCI_E_SUCCESS;
69 }
70 
wdt_set_enable(int enable)71 void wdt_set_enable(int enable)
72 {
73 	if (enable)
74 		wdt_pet();
75 	mmio_clrsetbits_32(WDT_MODE, WDT_MODE_EN,
76 			   WDT_MODE_KEY | (enable ? WDT_MODE_EN : 0));
77 }
78 
wdt_suspend(void)79 void wdt_suspend(void)
80 {
81 	wdt_enabled_before_suspend = mmio_read_32(WDT_MODE) & WDT_MODE_EN;
82 	if (wdt_enabled_before_suspend)
83 		wdt_set_enable(0);
84 }
85 
wdt_resume(void)86 void wdt_resume(void)
87 {
88 	if (wdt_enabled_before_suspend)
89 		wdt_set_enable(1);
90 }
91 
wdt_smc_handler(uint32_t x1,uint32_t x2,void * handle)92 uint64_t wdt_smc_handler(uint32_t x1,
93 			uint32_t x2,
94 			void *handle)
95 {
96 	int ret;
97 
98 	switch (x1) {
99 	case SMCWD_INFO:
100 		SMC_RET3(handle, PSCI_E_SUCCESS,
101 			 WDT_MIN_TIMEOUT, WDT_MAX_TIMEOUT);
102 	case SMCWD_SET_TIMEOUT:
103 		ret = wdt_set_timeout(x2);
104 		SMC_RET1(handle, ret);
105 	case SMCWD_ENABLE:
106 		wdt_set_enable(x2 > 0);
107 		SMC_RET1(handle, PSCI_E_SUCCESS);
108 	case SMCWD_PET:
109 		wdt_pet();
110 		SMC_RET1(handle, PSCI_E_SUCCESS);
111 	default:
112 		ERROR("Unimplemented SMCWD call (%d)\n", x1);
113 		SMC_RET1(handle, PSCI_E_NOT_SUPPORTED);
114 	}
115 }
116