/* SPDX-License-Identifier: GPL-2.0-only */ /* * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0. */ #include #include #include #include #include #include #include #include #include #include /* 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(>i->cc_imp_ctl, 0); write32(>i->cc_cntfid0, tickrate); write32(>i->ctl_cntfrq, tickrate); write32(>i->cc_cntrate, ((1ULL << 32) * tickrate) / sclk); /* Enable the counter */ setbits32(>i->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)); }