Newer
Older
mbed-os / targets / TARGET_Samsung / TARGET_SIDK_S5JS100 / watchdog_api.c
/****************************************************************************
 *
 * Copyright 2020 Samsung Electronics All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/
#include <string.h>
#include <stdio.h>
#include "cmsis_os.h"
#include "cmsis.h"

#if DEVICE_WATCHDOG
#define WATCHDOG_RESET_ENABLED  1
#include "watchdog_api.h"

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static void s5js100_watchdog_irq_enable(void);
static void s5js100_watchdog_irq_disable(void);
static void s5js100_watchdog_ack_irq(void);
static void s5js100_watchdog_reset_enable(void);
static void s5js100_watchdog_reset_disable(void);
static void s5js100_watchdog_enable(void);
static void s5js100_watchdog_disable(void);
static uint32_t s5js100_watchdog_getclock(void);
static void s5js100_watchdog_handler(void);
static void s5js100_watchdog_set_reload_val(uint32_t time_ms);
static void s5js100_watchdog_set_load_val_diff(uint32_t time_ms);
static uint32_t s5js100_watchdog_get_curr(void);
static void s5js100_watchdog_set_curr(unsigned int curr_val);
/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

// REGISTERS
#define rWDOG_CTRL              0x00
#define bWDOG_CNT_EN            (2)
#define bWDOG_PCLK_EN           (1)
#define bWDOG_WCLK_EN           (0)

#define rWDOG_LOAD_VAL          0x04
#define rWDOG_LOAD              0x08
#define rWDOG_RESET_REQ_EN      0x0C
#define rWDOG_INT_EN            0x10
#define rWDOG_INT_STAT_RAW      0x14
#define rWDOG_INT_STAT_MASKED   0x18
#define rWDOG_INT_CLR           0x1C
#define rWDOG_CNT_UPD_EN        0x20
#define rWDOG_CNT_VAL           0x24
#define rWDOG_RESET_REQN_STAT   0x28
#define rWDOG_LOAD_VAL_DIFF     0x2C
#define rWDOG_BLK_CTRL          0x800

#define EXT_SLPCLK      32768

/* Hold initially-configured timeout in hal_watchdog_init */
static uint32_t wdt_timeout_reload_ms = 0;


static uint32_t s5js100_watchdog_getclock(void)
{
    /* TODO: get TCLKB from CLK DRIVER */
    return EXT_SLPCLK;
}

watchdog_status_t hal_watchdog_init(const watchdog_config_t *config)
{
    /* Check validity of arguments */
    if (! config || ! config->timeout_ms) {
        return WATCHDOG_STATUS_INVALID_ARGUMENT;
    }

    wdt_timeout_reload_ms = config->timeout_ms;

    //clear Watchdog
    s5js100_watchdog_disable();

    // Set WDT interrupt
    NVIC_SetVector(S5JS100_IRQ_WDG, (uint32_t) s5js100_watchdog_handler);
#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
    SCB_InvalidateICache();
#endif
    NVIC_EnableIRQ(S5JS100_IRQ_WDG);

#if WATCHDOG_RESET_ENABLED
    s5js100_watchdog_reset_enable();
#endif
    s5js100_watchdog_set_reload_val(wdt_timeout_reload_ms);

    s5js100_watchdog_enable();

    return WATCHDOG_STATUS_OK;
}

void hal_watchdog_kick(void)
{
    s5js100_watchdog_set_reload_val(wdt_timeout_reload_ms);
}

watchdog_status_t hal_watchdog_stop(void)
{
    s5js100_watchdog_disable();
    return WATCHDOG_STATUS_OK;
}

uint32_t hal_watchdog_get_reload_value(void)
{
    return wdt_timeout_reload_ms;
}

watchdog_features_t hal_watchdog_get_platform_features(void)
{
    watchdog_features_t wdt_feat;

    /* We can support timeout values between 1 and UINT32_MAX by cascading. */
    wdt_feat.max_timeout = UINT32_MAX;
    /* Support re-configuring watchdog timer */
    wdt_feat.update_config = 1;
    /* Support stopping watchdog timer */
    wdt_feat.disable_watchdog = 1;

    return wdt_feat;
}

static void s5js100_watchdog_handler(void)
{
    s5js100_watchdog_ack_irq();
}

/****************************************************************************
 * Name: s5js100_watchdog_disable
 *
 * Description:
 *   Disable the watchdog timer. The S5J always boots with the watchdog
 *   timer enabled at timeout of 10 - 20 seconds by the second stage boot
 *   loader to detect any boot failure. So, the watchdog timer must be
 *   disabled as part of the start up logic.
 *
 ****************************************************************************/
static void s5js100_watchdog_disable(void)
{
    putreg32(0, S5JS100_WDT_BASE + rWDOG_CNT_UPD_EN);
    modifyreg32(S5JS100_WDT_BASE + rWDOG_CTRL, (1 << bWDOG_CNT_EN), 0);
}

/****************************************************************************
 * Name: s5js100_watchdog_enable
 *
 * Description:
 *   Enable watchdog operation.
 *   Should be correctly configured before enabling.
 *
 ****************************************************************************/
static void s5js100_watchdog_enable(void)
{
    putreg32(1, S5JS100_WDT_BASE + rWDOG_CNT_UPD_EN);
    modifyreg32(S5JS100_WDT_BASE + rWDOG_CTRL, (0x1 << bWDOG_CNT_EN), (0x1 << bWDOG_CNT_EN));
}

/****************************************************************************
 * Name: s5js100_watchdog_reset_disable
 *
 * Description:
 *   When WD timer expires, it can issue HW reset.
 *   This function disables reset feature.
 *   Watchdog will be reloaded with value written in reload register.
 *   and continue its operation.
 *
 ****************************************************************************/
static void s5js100_watchdog_reset_disable(void)
{
    putreg32(0, S5JS100_WDT_BASE + rWDOG_RESET_REQ_EN);
}

/****************************************************************************
 * Name: s5js100_watchdog_reset_enable
 *
 * Description:
 *   When WD timer expires, it can issue HW reset.
 *   This function enables reset feature.
 *
 ****************************************************************************/
static void s5js100_watchdog_reset_enable(void)
{
    putreg32(1, S5JS100_WDT_BASE + rWDOG_RESET_REQ_EN);
    putreg32(1 << 1, 0x82020018);

}

static void s5js100_watchdog_ack_irq(void)
{
    putreg32(0, S5JS100_WDT_BASE + rWDOG_INT_CLR);
}

/****************************************************************************
 * Name: s5js100_watchdog_irq_disable
 *
 * Description:
 *   When WD timer expires, it can issue interrupt.
 *   This function disables reset feature.
 *
 ****************************************************************************/
static void s5js100_watchdog_irq_disable(void)
{
    putreg32(0, S5JS100_WDT_BASE + rWDOG_INT_EN);
}

/****************************************************************************
 * Name: s5js100_watchdog_irq_enable
 *
 * Description:
 *   When WD timer expires, it can issue interrupt.
 *   This function enables reset feature.
 *
 ****************************************************************************/
static void s5js100_watchdog_irq_enable(void)
{
    putreg32(1, S5JS100_WDT_BASE + rWDOG_INT_EN);
}

/****************************************************************************
 * Name: s5js100_watchdog_set_reload_val
 *
 * Description:
 *   When WD timer expires, if reset is disabled, will be reloaded with value
 *   defined by this function call.
 *
 ****************************************************************************/
static void s5js100_watchdog_set_reload_val(uint32_t time_ms)
{
    uint32_t slp_clk = s5js100_watchdog_getclock();
    uint32_t load_value;

    load_value = (time_ms * slp_clk) / 1000;
    putreg32(load_value, S5JS100_WDT_BASE + rWDOG_LOAD_VAL);
    putreg32(0x1, S5JS100_WDT_BASE + rWDOG_LOAD);
}

/****************************************************************************
 * Name: s5js100_watchdog_set_load_val_diff
 *
 * Description:
 *  Funtion s5js100_watchdog_set_load_val_diff set LOAD_VAL_DIFF value.
 *  This value sets the difference between interrupt assertion time and
 *  the reset request assertion time. The interrupt assertion occurs before
 *  the reset request as this value. This value should be stable before writing
 *  1 to LOAD register.
 *
 ****************************************************************************/
static void s5js100_watchdog_set_load_val_diff(uint32_t time_ms)
{
    uint32_t slp_clk = s5js100_watchdog_getclock();
    uint32_t load_diff;

    load_diff = (time_ms * slp_clk) / 1000;
    putreg32(load_diff, S5JS100_WDT_BASE + rWDOG_LOAD_VAL_DIFF);
}

/****************************************************************************
 * Name: s5js100_watchdog_get_curr
 *
 * Description:
 *   Function s5js100_watchdog_get_curr returns current WD counter value.
 ****************************************************************************/
static uint32_t s5js100_watchdog_get_curr(void)
{
    uint32_t slp_clk = s5js100_watchdog_getclock();
    uint32_t load_value;

    load_value = getreg32(S5JS100_WDT_BASE + rWDOG_CNT_VAL);

    return ((load_value * 1000) / slp_clk);
}

/****************************************************************************
 * Name: s5js100_watchdog_set_curr
 *
 * Description:
 *   Function s5js100_watchdog_set_curr sets immediately current WD counter value.
 *   Use this function to set initial WD timer value before running operation.
 ****************************************************************************/
static void s5js100_watchdog_set_curr(unsigned int curr_val)
{
    putreg32(curr_val, S5JS100_WDT_BASE + rWDOG_LOAD_VAL);
    putreg32(0x1, S5JS100_WDT_BASE + rWDOG_LOAD);
}

#endif