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); 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 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 struct itr_handler *wdt_itr; 106 107 assert(!pd->chip.wdt_itr); 108 109 wdt_itr = calloc(1, sizeof(*wdt_itr)); 110 if (!wdt_itr) 111 return TEE_ERROR_OUT_OF_MEMORY; 112 113 wdt_itr->it = itr_num; 114 wdt_itr->flags = itr_flags; 115 wdt_itr->handler = wdt_itr_cb; 116 wdt_itr->data = &pd->chip; 117 pd->itr_handler = itr_handler; 118 pd->chip.wdt_itr = wdt_itr; 119 120 itr_add(wdt_itr); 121 itr_enable(wdt_itr->it); 122 123 return TEE_SUCCESS; 124 } 125 126 static const struct wdt_ops sp805_wdt_ops = { 127 .start = sp805_enable, 128 .stop = sp805_disable, 129 .ping = sp805_ping, 130 .set_timeout = sp805_setload, 131 }; 132 KEEP_PAGER(sp805_wdt_ops); 133 134 TEE_Result sp805_wdt_init(struct sp805_wdt_data *pd, paddr_t base, 135 uint32_t clk_rate, uint32_t timeout) 136 { 137 assert(pd); 138 pd->base.pa = base; 139 pd->clk_rate = clk_rate; 140 pd->chip.ops = &sp805_wdt_ops; 141 return sp805_setload(&pd->chip, timeout); 142 } 143