blob: d9879baec6ab04211bd9b7de2cda416e88c16380 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
*/
#include <delay.h>
#include <device/mmio.h>
#include <arch/lib_helpers.h>
#include <console/console.h>
#include <soc/clock.h>
#include <soc/timer.h>
#include <stdint.h>
#include <timer.h>
#include <soc/addressmap.h>
#include <assert.h>
/* Global System Timers Unit (GTI) registers */
struct cn81xx_timer {
u32 cc_cntcr;
u32 cc_cntsr;
u64 cc_cntcv;
u8 rsvd[0x10];
u32 cc_cntfid0;
u32 cc_cntfid1;
u8 rsvd2[0x98];
u32 cc_cntrate;
u32 cc_cntracc;
u64 cc_cntadd;
u64 cc_cntmb;
u64 cc_cntmbts;
u64 cc_cntmb_int;
u64 cc_cntmb_int_set;
u64 cc_cntmb_int_ena_clr;
u64 cc_cntmb_int_ena_set;
u64 cc_imp_ctl;
u8 skip[0x1fef8];
u32 ctl_cntfrq;
u32 ctl_cntnsar;
u32 ctl_cnttidr;
u8 rsvd3[0x34];
u32 ctl_cntacr0;
u8 skip2[0x1ffb8];
u64 cwd_wdog[48]; /* Offset 0x40000 */
u8 skip3[0xfe80];
u64 cwd_poke[48]; /* Offset 0x50000 */
};
check_member(cn81xx_timer, cc_imp_ctl, 0x100);
check_member(cn81xx_timer, ctl_cntacr0, 0x20040);
check_member(cn81xx_timer, cwd_wdog[0], 0x40000);
check_member(cn81xx_timer, cwd_poke[0], 0x50000);
#define GTI_CC_CNTCR_EN (1 << 0)
#define GTI_CC_CNTCR_HDBG (1 << 1)
#define GTI_CC_CNTCR_FCREQ (1 << 8)
#define GTI_CC_CNTSR_DBGH (1 << 1)
#define GTI_CC_CNTSR_FCACK (1 << 8)
#define GTI_CWD_WDOG_MODE_SHIFT 0
#define GTI_CWD_WDOG_MODE_MASK 0x3
#define GTI_CWD_WDOG_STATE_SHIFT 2
#define GTI_CWD_WDOG_STATE_MASK 0x3
#define GTI_CWD_WDOG_LEN_SHIFT 4
#define GTI_CWD_WDOG_LEN_MASK 0xffff
#define GTI_CWD_WDOG_CNT_SHIFT 20
#define GTI_CWD_WDOG_CNT_MASK 0xffffff
#define GTI_CWD_WDOC_DSTOP (1 << 44)
#define GTI_CWD_WDOC_GSTOP (1 << 45)
static uint64_t timer_raw_value(void)
{
struct cn81xx_timer *timer = (void *)GTI_PF_BAR0;
return read64(&timer->cc_cntcv);
}
/**
* Get GTI counter value.
* @param mt Structure to fill
*/
void timer_monotonic_get(struct mono_time *mt)
{
mono_time_set_usecs(mt, timer_raw_value());
}
/* Setup counter to operate at 1MHz */
static const size_t tickrate = 1000000;
/**
* Init Global System Timers Unit (GTI).
* Configure timer to run at 1MHz tick-rate.
*/
void init_timer(void)
{
struct cn81xx_timer *gti = (struct cn81xx_timer *)GTI_PF_BAR0;
/* Check if the counter was already setup */
if (gti->cc_cntcr & GTI_CC_CNTCR_EN)
return;
u64 sclk = thunderx_get_io_clock();
/* Use coprocessor clock source */
write32(&gti->cc_imp_ctl, 0);
write32(&gti->cc_cntfid0, tickrate);
write32(&gti->ctl_cntfrq, tickrate);
write32(&gti->cc_cntrate, ((1ULL << 32) * tickrate) / sclk);
/* Enable the counter */
setbits32(&gti->cc_cntcr, GTI_CC_CNTCR_EN);
//u32 u = (CNTPS_CTL_EL1_IMASK | CNTPS_CTL_EL1_EN);
//BDK_MSR(CNTPS_CTL_EL1, u);
}
void soc_timer_init(void)
{
raw_write_cntfrq_el0(tickrate);
}
/**
* Setup the watchdog to expire in timeout_ms milliseconds. When the watchdog
* expires, the chip three things happen:
* 1) Expire 1: interrupt that is ignored by the BDK
* 2) Expire 2: DEL3T interrupt, which is disabled and ignored
* 3) Expire 3: Soft reset of the chip
*
* Since we want a soft reset, we actually program the watchdog to expire at
* the timeout / 3.
*
* @param index Index of watchdog to configure
* @param timeout_ms Timeout in milliseconds.
*/
void watchdog_set(const size_t index, unsigned int timeout_ms)
{
uint64_t sclk = thunderx_get_io_clock();
uint64_t timeout_sclk = sclk * timeout_ms / 1000;
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_wdog));
if (index >= ARRAY_SIZE(timer->cwd_wdog))
return;
/*
* Per comment above, we want the watchdog to expire at 3x the rate
* specified
*/
timeout_sclk /= 3;
/* Watchdog counts in 1024 cycle steps */
uint64_t timeout_wdog = timeout_sclk >> 10;
/* We can only specify the upper 16 bits of a 24 bit value. Round up */
timeout_wdog = (timeout_wdog + 0xff) >> 8;
/* If the timeout overflows the hardware limit, set max */
if (timeout_wdog >= 0x10000)
timeout_wdog = 0xffff;
printk(BIOS_DEBUG, "Watchdog: Set to expire %llu SCLK cycles\n",
timeout_wdog << 18);
clrsetbits64(&timer->cwd_wdog[index],
(GTI_CWD_WDOG_LEN_MASK << GTI_CWD_WDOG_LEN_SHIFT) |
(GTI_CWD_WDOG_MODE_MASK << GTI_CWD_WDOG_MODE_SHIFT),
(timeout_wdog << GTI_CWD_WDOG_LEN_SHIFT) |
(3 << GTI_CWD_WDOG_MODE_SHIFT));
}
/**
* Signal the watchdog that we are still running.
*
* @param index Index of watchdog to configure.
*/
void watchdog_poke(const size_t index)
{
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_poke));
if (index >= ARRAY_SIZE(timer->cwd_poke))
return;
write64(&timer->cwd_poke[0], 0);
}
/**
* Disable the hardware watchdog
*
* @param index Index of watchdog to configure.
*/
void watchdog_disable(const size_t index)
{
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_wdog));
if (index >= ARRAY_SIZE(timer->cwd_wdog))
return;
write64(&timer->cwd_wdog[index], 0);
printk(BIOS_DEBUG, "Watchdog: Disabled\n");
}
/**
* Return true if the watchdog is configured and running
*
* @param index Index of watchdog to configure.
*
* @return Non-zero if watchdog is running.
*/
int watchdog_is_running(const size_t index)
{
struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;
assert(index < ARRAY_SIZE(timer->cwd_wdog));
if (index >= ARRAY_SIZE(timer->cwd_wdog))
return 0;
uint64_t val = read64(&timer->cwd_wdog[index]);
return !!(val & (GTI_CWD_WDOG_MODE_MASK << GTI_CWD_WDOG_MODE_SHIFT));
}