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