233 lines
6.0 KiB
C
233 lines
6.0 KiB
C
/***************************************************************************
|
|
*
|
|
* 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 "hal_wdt.h"
|
|
#include "cmsis.h"
|
|
#include "cmsis_nvic.h"
|
|
#include "hal_timer.h"
|
|
#include "hal_trace.h"
|
|
#include "hal_uart.h"
|
|
#include "plat_addr_map.h"
|
|
#include "plat_types.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
|