bdk: timer: add timer/watchdog driver

This commit is contained in:
CTCaer 2022-06-27 10:20:25 +03:00
parent b65b2d7f71
commit 061e10152f
6 changed files with 192 additions and 66 deletions

View file

@ -31,9 +31,10 @@ OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \
# Hardware. # Hardware.
OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \ OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \
bpmp.o ccplex.o clock.o di.o gpio.o i2c.o irq.o mc.o sdram.o \ bpmp.o ccplex.o clock.o di.o i2c.o irq.o timer.o \
pinmux.o pmc.o se.o smmu.o tsec.o uart.o \ mc.o sdram.o minerva.o \
fuse.o kfuse.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 \ sdmmc.o sdmmc_driver.o emmc.o sd.o emummc.o \
bq24193.o max17050.o max7762x.o max77620-rtc.o \ bq24193.o max17050.o max7762x.o max77620-rtc.o \
hw_init.o \ hw_init.o \

114
bdk/soc/timer.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <soc/bpmp.h>
#include <soc/irq.h>
#include <soc/timer.h>
#include <soc/t210.h>
#include <utils/types.h>
#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);
}

62
bdk/soc/timer.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef _TIMER_H_
#define _TIMER_H_
#include <utils/types.h>
// 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

View file

@ -24,14 +24,13 @@
#include <soc/hw_init.h> #include <soc/hw_init.h>
#include <soc/i2c.h> #include <soc/i2c.h>
#include <soc/pmc.h> #include <soc/pmc.h>
#include <soc/timer.h>
#include <soc/t210.h> #include <soc/t210.h>
#include <storage/sd.h> #include <storage/sd.h>
#include <utils/util.h> #include <utils/util.h>
#define USE_RTC_TIMER #define USE_RTC_TIMER
extern volatile nyx_storage_t *nyx_str;
u8 bit_count(u32 val) u8 bit_count(u32 val)
{ {
u8 cnt = 0; u8 cnt = 0;
@ -103,51 +102,6 @@ u64 sqrt64(u64 num)
return square_root; 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) void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops)
{ {
for(u32 i = 0; i < num_ops; i++) for(u32 i = 0; i < num_ops; i++)
@ -195,14 +149,14 @@ void panic(u32 val)
{ {
// Set panic code. // Set panic code.
PMC(APBDEV_PMC_SCRATCH200) = val; 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) // Disable SE.
usleep(1); //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) void power_set_state(power_state_t state)
@ -231,7 +185,7 @@ void power_set_state(power_state_t state)
break; break;
case POWER_OFF: 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); i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_PWR_OFF);
break; break;
@ -246,7 +200,7 @@ void power_set_state(power_state_t state)
reg |= MAX77620_ONOFFCNFG2_SFT_RST_WK; reg |= MAX77620_ONOFFCNFG2_SFT_RST_WK;
i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG2, reg); 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); i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_SFT_RST);
break; break;
} }

View file

@ -89,12 +89,6 @@ u64 sqrt64(u64 num);
void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops); void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops);
u32 crc32_calc(u32 crc, const u8 *buf, u32 len); 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 panic(u32 val);
void power_set_state(power_state_t state); void power_set_state(power_state_t state);
void power_set_state_ex(void *param); void power_set_state_ex(void *param);

View file

@ -33,7 +33,8 @@ OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \
# Hardware. # Hardware.
OBJS += $(addprefix $(BUILDDIR)/$(TARGET)/, \ 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 \ fuse.o kfuse.o \
mc.o sdram.o minerva.o ramdisk.o \ mc.o sdram.o minerva.o ramdisk.o \
sdmmc.o sdmmc_driver.o emmc.o sd.o nx_emmc_bis.o \ sdmmc.o sdmmc_driver.o emmc.o sd.o nx_emmc_bis.o \