/*************************************************************************** * * Copyright 2015-2019 BES. * All rights reserved. All unpublished rights reserved. * * No part of this work may be used or reproduced in any form or by any * means, or stored in a database or retrieval system, without prior written * permission of BES. * * Use of this work is governed by a license granted by BES. * This work contains confidential and proprietary information of * BES. which is protected by copyright, trade secret, * trademark and other intellectual property rights. * ****************************************************************************/ #include "plat_types.h" #include "plat_addr_map.h" #include "cmsis.h" #include "cmsis_nvic.h" #include "hal_timer.h" #include "hal_trace.h" #include "hal_uart.h" #include "hal_wdt.h" #if defined(CHIP_BEST3001) || defined(CHIP_BEST3003) || defined(CHIP_BEST3005) || defined(CHIP_BEST1400) || defined(CHIP_BEST1402) #define CLOCK_SYNC_WORKAROUND #endif #define SLOW_TIMER_VAL_DELTA 1 #define FAST_TIMER_VAL_DELTA 20 /* wdt controller */ /* reg address */ /* default timeout in seconds */ #define DEFAULT_TIMEOUT 60 #define WDT_RATE CONFIG_SYSTICK_HZ /* watchdog register offsets and masks */ #define WDTLOAD_REG_OFFSET 0x000 #define WDTLOAD_LOAD_MIN 0x00000001 #define WDTLOAD_LOAD_MAX 0xFFFFFFFF #define WDTVALUE_REG_OFFSET 0x004 #define WDTCONTROL_REG_OFFSET 0x008 #define WDTCONTROL_REG_INT_ENABLE (1 << 0) #define WDTCONTROL_REG_RESET_ENABLE (1 << 1) #define WDTINTCLR_REG_OFFSET 0x00C #define WDTRIS_REG_OFFSET 0x010 #define WDTRIS_REG_INT_MASK (1 << 0) #define WDTMIS_REG_OFFSET 0x014 #define WDTMIS_REG_INT_MASK (1 << 0) #define WDTLOCK_REG_OFFSET 0xC00 #define WDTLOCK_REG_UNLOCK 0x1ACCE551 #define WDTLOCK_REG_LOCK 0x00000001 /* read write */ #define wdtip_write32(v,b,a) \ (*((volatile unsigned int *)(b+a)) = v) #define wdtip_read32(b,a) \ (*((volatile unsigned int *)(b+a))) #if 1 typedef void (*HAL_WDT_IRQ_HANDLER)(void); struct HAL_WDT_CTX { unsigned int load_val; }; struct HAL_WDT_CTX hal_wdt_ctx[HAL_WDT_ID_NUM]; static void _wdt1_irq_handler(void); HAL_WDT_IRQ_HANDLER hal_wdt_irq_handler[HAL_WDT_ID_NUM] = { _wdt1_irq_handler, }; HAL_WDT_IRQ_CALLBACK hal_wdt_irq_callback[HAL_WDT_ID_NUM]; static void _wdt1_irq_handler(void) { if (hal_wdt_irq_callback[HAL_WDT_ID_0] != 0) { hal_wdt_irq_callback[HAL_WDT_ID_0](HAL_WDT_ID_0, HAL_WDT_EVENT_FIRE); } } static unsigned int _wdt_get_base(enum HAL_WDT_ID_T id) { switch(id) { default: case HAL_WDT_ID_0: return WDT_BASE; break; } return 0; } static void _wdt_load(enum HAL_WDT_ID_T id) { unsigned int reg_base = 0; struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id]; uint32_t lock; reg_base = _wdt_get_base(id); lock = int_lock(); wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET); #ifdef CLOCK_SYNC_WORKAROUND uint32_t val; do { wdtip_write32(wdt->load_val, reg_base, WDTLOAD_REG_OFFSET); val = wdtip_read32(reg_base, WDTVALUE_REG_OFFSET); } while ((wdt->load_val < val) || (wdt->load_val > val + SLOW_TIMER_VAL_DELTA)); #else wdtip_write32(wdt->load_val, reg_base, WDTLOAD_REG_OFFSET); #endif wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET); int_unlock(lock); /* Flush posted writes. */ wdtip_read32(reg_base, WDTLOCK_REG_OFFSET); } static void _wdt_config(enum HAL_WDT_ID_T id) { unsigned int reg_base = 0; uint32_t lock; reg_base = _wdt_get_base(id); lock = int_lock(); wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET); wdtip_write32(WDTRIS_REG_INT_MASK, reg_base, WDTINTCLR_REG_OFFSET); wdtip_write32(WDTCONTROL_REG_INT_ENABLE | WDTCONTROL_REG_RESET_ENABLE, reg_base, WDTCONTROL_REG_OFFSET); wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET); int_unlock(lock); /* Flush posted writes. */ wdtip_read32(reg_base, WDTLOCK_REG_OFFSET); } static int wdt_start; /* mandatory operations */ int hal_wdt_start(enum HAL_WDT_ID_T id) { _wdt_load(id); _wdt_config(id); wdt_start = 1; return 0; } int hal_wdt_stop(enum HAL_WDT_ID_T id) { unsigned int reg_base = 0; uint32_t lock; reg_base = _wdt_get_base(id); lock = int_lock(); wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET); wdtip_write32(0, reg_base, WDTCONTROL_REG_OFFSET); wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET); int_unlock(lock); /* Flush posted writes. */ wdtip_read32(reg_base, WDTLOCK_REG_OFFSET); wdt_start = 0; return 0; } /* optional operations */ int hal_wdt_ping(enum HAL_WDT_ID_T id) { if (wdt_start) { _wdt_load(id); } return 0; } int hal_wdt_set_timeout(enum HAL_WDT_ID_T id, unsigned int timeout) { uint64_t load; struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id]; /* * sp805 runs counter with given value twice, after the end of first * counter it gives an interrupt and then starts counter again. If * interrupt already occurred then it resets the system. This is why * load is half of what should be required. */ load = WDT_RATE * timeout - 1; load = (load > WDTLOAD_LOAD_MAX) ? WDTLOAD_LOAD_MAX: load; load = (load < WDTLOAD_LOAD_MIN) ? WDTLOAD_LOAD_MIN: load; wdt->load_val = load; return 0; } unsigned int hal_wdt_get_timeleft(enum HAL_WDT_ID_T id) { uint64_t load; unsigned int reg_base = 0; struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id]; reg_base = _wdt_get_base(id); load = wdtip_read32(reg_base, WDTVALUE_REG_OFFSET); /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */ if (!(wdtip_read32(reg_base, WDTRIS_REG_OFFSET) & WDTRIS_REG_INT_MASK)) load += wdt->load_val + 1; return load/WDT_RATE; } void hal_wdt_set_irq_callback(enum HAL_WDT_ID_T id, HAL_WDT_IRQ_CALLBACK cb) { switch(id) { default: case HAL_WDT_ID_0: NVIC_SetVector(WDT_IRQn, (uint32_t)hal_wdt_irq_handler[id]); NVIC_SetPriority(WDT_IRQn, IRQ_PRIORITY_NORMAL); NVIC_ClearPendingIRQ(WDT_IRQn); NVIC_EnableIRQ(WDT_IRQn); break; } hal_wdt_irq_callback[id] = cb; } #endif