From 061e10152f5bf236844ac45d64f7c54346642e0a Mon Sep 17 00:00:00 2001 From: CTCaer Date: Mon, 27 Jun 2022 10:20:25 +0300 Subject: [PATCH] bdk: timer: add timer/watchdog driver --- Makefile | 7 +-- bdk/soc/timer.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ bdk/soc/timer.h | 62 ++++++++++++++++++++++++++ bdk/utils/util.c | 66 +++++---------------------- bdk/utils/util.h | 6 --- nyx/Makefile | 3 +- 6 files changed, 192 insertions(+), 66 deletions(-) create mode 100644 bdk/soc/timer.c create mode 100644 bdk/soc/timer.h diff --git a/Makefile b/Makefile index 1983aaf..d92bdf5 100755 --- a/Makefile +++ b/Makefile @@ -31,9 +31,10 @@ OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \ # Hardware. OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \ - bpmp.o ccplex.o clock.o di.o gpio.o i2c.o irq.o mc.o sdram.o \ - pinmux.o pmc.o se.o smmu.o tsec.o uart.o \ - fuse.o kfuse.o minerva.o \ + bpmp.o ccplex.o clock.o di.o i2c.o irq.o timer.o \ + mc.o sdram.o minerva.o \ + gpio.o pinmux.o pmc.o se.o smmu.o tsec.o uart.o \ + fuse.o kfuse.o \ sdmmc.o sdmmc_driver.o emmc.o sd.o emummc.o \ bq24193.o max17050.o max7762x.o max77620-rtc.o \ hw_init.o \ diff --git a/bdk/soc/timer.c b/bdk/soc/timer.c new file mode 100644 index 0000000..5c3338e --- /dev/null +++ b/bdk/soc/timer.c @@ -0,0 +1,114 @@ +/* + * Timer/Watchdog driver for Tegra X1 + * + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#define EXCP_TYPE_ADDR 0x4003FFF8 +#define EXCP_TYPE_WDT 0x544457 // "WDT". + +u32 get_tmr_s() +{ + (void)RTC(APBDEV_RTC_MILLI_SECONDS); + return (u32)RTC(APBDEV_RTC_SECONDS); +} + +u32 get_tmr_ms() +{ + // The registers must be read with the following order: + // RTC_MILLI_SECONDS (0x10) -> RTC_SHADOW_SECONDS (0xC) + return (u32)(RTC(APBDEV_RTC_MILLI_SECONDS) + (RTC(APBDEV_RTC_SHADOW_SECONDS) * 1000)); +} + +u32 get_tmr_us() +{ + return (u32)TMR(TIMERUS_CNTR_1US); +} + +void msleep(u32 ms) +{ +#ifdef USE_RTC_TIMER + u32 start = (u32)RTC(APBDEV_RTC_MILLI_SECONDS) + (RTC(APBDEV_RTC_SHADOW_SECONDS) * 1000); + // Casting to u32 is important! + while (((u32)(RTC(APBDEV_RTC_MILLI_SECONDS) + (RTC(APBDEV_RTC_SHADOW_SECONDS) * 1000)) - start) <= ms) + ; +#else + bpmp_msleep(ms); +#endif +} + +void usleep(u32 us) +{ +#ifdef USE_RTC_TIMER + u32 start = (u32)TMR(TIMERUS_CNTR_1US); + + // Check if timer is at upper limits and use BPMP sleep so it doesn't wake up immediately. + if ((start + us) < start) + bpmp_usleep(us); + else + while ((u32)(TMR(TIMERUS_CNTR_1US) - start) <= us) // Casting to u32 is important! + ; +#else + bpmp_usleep(us); +#endif +} + +void timer_usleep(u32 us) +{ + TMR(TIMER_TMR8_TMR_PTV) = TIMER_EN | us; + + irq_wait_event(IRQ_TMR8); + + TMR(TIMER_TMR8_TMR_PCR) = TIMER_INTR_CLR; +} + +void watchdog_start(u32 us, u32 mode) +{ + // WDT4 is for BPMP. + TMR(TIMER_WDT4_UNLOCK_PATTERN) = TIMER_MAGIC_PTRN; + TMR(TIMER_TMR9_TMR_PTV) = TIMER_EN | TIMER_PER_EN | us; + TMR(TIMER_WDT4_CONFIG) = TIMER_SRC(9) | TIMER_PER(1) | mode; + TMR(TIMER_WDT4_COMMAND) = TIMER_START_CNT; +} + +void watchdog_end() +{ + // WDT4 is for BPMP. + TMR(TIMER_TMR9_TMR_PTV) = 0; + TMR(TIMER_WDT4_UNLOCK_PATTERN) = TIMER_MAGIC_PTRN; + TMR(TIMER_WDT4_COMMAND) = TIMER_START_CNT; // Re-arm to clear any interrupts. + TMR(TIMER_WDT4_COMMAND) = TIMER_CNT_DISABLE; +} + +void watchdog_handle() +{ + // Disable watchdog and clear its interrupts. + watchdog_end(); + + // Set watchdog magic. + *(u32 *)EXCP_TYPE_ADDR = EXCP_TYPE_WDT; +} + +bool watchdog_fired() +{ + // Return if watchdog got fired. User handles clearing. + return (*(u32 *)EXCP_TYPE_ADDR == EXCP_TYPE_WDT); +} \ No newline at end of file diff --git a/bdk/soc/timer.h b/bdk/soc/timer.h new file mode 100644 index 0000000..6fcdb36 --- /dev/null +++ b/bdk/soc/timer.h @@ -0,0 +1,62 @@ +/* + * Timer/Watchdog driver for Tegra X1 + * + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include + +// TMR registers. +#define TIMERUS_CNTR_1US (0x10 + 0x0) +#define TIMERUS_USEC_CFG (0x10 + 0x4) +#define TIMER_TMR8_TMR_PTV 0x78 +#define TIMER_TMR9_TMR_PTV 0x80 +#define TIMER_PER_EN BIT(30) +#define TIMER_EN BIT(31) +#define TIMER_TMR8_TMR_PCR 0x7C +#define TIMER_TMR9_TMR_PCR 0x8C +#define TIMER_INTR_CLR BIT(30) + +// WDT registers. +#define TIMER_WDT4_CONFIG (0x100 + 0x80) +#define TIMER_SRC(TMR) ((TMR) & 0xF) +#define TIMER_PER(PER) (((PER) & 0xFF) << 4) +#define TIMER_IRQENABL_EN BIT(12) +#define TIMER_FIQENABL_EN BIT(13) +#define TIMER_SYSRESET_EN BIT(14) +#define TIMER_PMCRESET_EN BIT(15) +#define TIMER_WDT4_COMMAND (0x108 + 0x80) +#define TIMER_START_CNT BIT(0) +#define TIMER_CNT_DISABLE BIT(1) +#define TIMER_WDT4_UNLOCK_PATTERN (0x10C + 0x80) +#define TIMER_MAGIC_PTRN 0xC45A + +u32 get_tmr_us(); +u32 get_tmr_ms(); +u32 get_tmr_s(); +void usleep(u32 us); +void msleep(u32 ms); + +void timer_usleep(u32 us); + +void watchdog_start(u32 us, u32 mode); +void watchdog_end(); +void watchdog_handle(); +bool watchdog_fired(); + +#endif diff --git a/bdk/utils/util.c b/bdk/utils/util.c index d249f9d..623d8e4 100644 --- a/bdk/utils/util.c +++ b/bdk/utils/util.c @@ -24,14 +24,13 @@ #include #include #include +#include #include #include #include #define USE_RTC_TIMER -extern volatile nyx_storage_t *nyx_str; - u8 bit_count(u32 val) { u8 cnt = 0; @@ -103,51 +102,6 @@ u64 sqrt64(u64 num) return square_root; } -u32 get_tmr_s() -{ - return RTC(APBDEV_RTC_SECONDS); -} - -u32 get_tmr_ms() -{ - // The registers must be read with the following order: - // RTC_MILLI_SECONDS (0x10) -> RTC_SHADOW_SECONDS (0xC) - return (RTC(APBDEV_RTC_MILLI_SECONDS) + (RTC(APBDEV_RTC_SHADOW_SECONDS) * 1000)); -} - -u32 get_tmr_us() -{ - return TMR(TIMERUS_CNTR_1US); -} - -void msleep(u32 ms) -{ -#ifdef USE_RTC_TIMER - u32 start = RTC(APBDEV_RTC_MILLI_SECONDS) + (RTC(APBDEV_RTC_SHADOW_SECONDS) * 1000); - // Casting to u32 is important! - while (((u32)(RTC(APBDEV_RTC_MILLI_SECONDS) + (RTC(APBDEV_RTC_SHADOW_SECONDS) * 1000)) - start) <= ms) - ; -#else - bpmp_msleep(ms); -#endif -} - -void usleep(u32 us) -{ -#ifdef USE_RTC_TIMER - u32 start = TMR(TIMERUS_CNTR_1US); - - // Check if timer is at upper limits and use BPMP sleep so it doesn't wake up immediately. - if ((start + us) < start) - bpmp_usleep(us); - else - while ((u32)(TMR(TIMERUS_CNTR_1US) - start) <= us) // Casting to u32 is important! - ; -#else - bpmp_usleep(us); -#endif -} - void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops) { for(u32 i = 0; i < num_ops; i++) @@ -195,14 +149,14 @@ void panic(u32 val) { // Set panic code. PMC(APBDEV_PMC_SCRATCH200) = val; - //PMC(APBDEV_PMC_CRYPTO_OP) = PMC_CRYPTO_OP_SE_DISABLE; - TMR(TIMER_WDT4_UNLOCK_PATTERN) = TIMER_MAGIC_PTRN; - TMR(TIMER_TMR9_TMR_PTV) = TIMER_EN | TIMER_PER_EN; - TMR(TIMER_WDT4_CONFIG) = TIMER_SRC(9) | TIMER_PER(1) | TIMER_PMCRESET_EN; - TMR(TIMER_WDT4_COMMAND) = TIMER_START_CNT; - while (true) - usleep(1); + // Disable SE. + //PMC(APBDEV_PMC_CRYPTO_OP) = PMC_CRYPTO_OP_SE_DISABLE; + + // Immediately cause a full system reset. + watchdog_start(0, TIMER_PMCRESET_EN); + + while (true); } void power_set_state(power_state_t state) @@ -231,7 +185,7 @@ void power_set_state(power_state_t state) break; case POWER_OFF: - // Initiate power down sequence and do not generate a reset (regulators retain state). + // Initiate power down sequence and do not generate a reset (regulators retain state after POR). i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_PWR_OFF); break; @@ -246,7 +200,7 @@ void power_set_state(power_state_t state) reg |= MAX77620_ONOFFCNFG2_SFT_RST_WK; i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG2, reg); - // Initiate power down sequence and generate a reset (regulators' state resets). + // Initiate power down sequence and generate a reset (regulators' state resets after POR). i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_SFT_RST); break; } diff --git a/bdk/utils/util.h b/bdk/utils/util.h index d942f2d..57ef250 100644 --- a/bdk/utils/util.h +++ b/bdk/utils/util.h @@ -89,12 +89,6 @@ u64 sqrt64(u64 num); void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops); u32 crc32_calc(u32 crc, const u8 *buf, u32 len); -u32 get_tmr_us(); -u32 get_tmr_ms(); -u32 get_tmr_s(); -void usleep(u32 us); -void msleep(u32 ms); - void panic(u32 val); void power_set_state(power_state_t state); void power_set_state_ex(void *param); diff --git a/nyx/Makefile b/nyx/Makefile index 0d5c152..9928a97 100644 --- a/nyx/Makefile +++ b/nyx/Makefile @@ -33,7 +33,8 @@ OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \ # Hardware. OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \ - bpmp.o ccplex.o clock.o di.o gpio.o i2c.o irq.o pinmux.o pmc.o se.o smmu.o tsec.o uart.o \ + bpmp.o ccplex.o clock.o di.o i2c.o irq.o timer.o \ + gpio.o pinmux.o pmc.o se.o smmu.o tsec.o uart.o \ fuse.o kfuse.o \ mc.o sdram.o minerva.o ramdisk.o \ sdmmc.o sdmmc_driver.o emmc.o sd.o nx_emmc_bis.o \