nuttx/arch/arm/src/lc823450/lc823450_timer.c
Xiang Xiao 5b2a17b892 Include assert.h in necessary place
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2021-06-08 13:06:08 -07:00

729 lines
19 KiB
C

/****************************************************************************
* arch/arm/src/lc823450/lc823450_timer.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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 <nuttx/config.h>
#include <stdint.h>
#include <stddef.h>
#include <time.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/spinlock.h>
#include <arch/board/board.h>
#include "nvic.h"
#include "clock/clock.h"
#include "arm_internal.h"
#include "arm_arch.h"
#include "chip.h"
#include "lc823450_gpio.h"
#ifdef CONFIG_LC823450_MTM0_TICK
# include "lc823450_pwm.h"
#endif
#include "lc823450_syscontrol.h"
#include "lc823450_clockconfig.h"
#include "lc823450_serial.h"
#include "lc823450_timer.h"
#ifdef CONFIG_DVFS
# include "lc823450_dvfs2.h"
#endif
#if !defined(CONFIG_LC823450_MTM0_TICK) && defined (CONFIG_DVFS)
# error "Use CONFIG_LC823450_MTM0_TICK=y"
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define SYSTICK_RELOAD ((lc823450_get_systemfreq() / CLK_TCK) - 1)
/* TIMER_PIN will be used to check the interval */
#define TIMER_PIN (GPIO_PORT5|GPIO_PIN7)
/* #define CHECK_INTERVAL */
#ifdef CONFIG_LC823450_MTM0_TICK
# define MT00STS (LC823450_MTM0_REGBASE + LC823450_MTM_0STS)
# define MT00A (LC823450_MTM0_REGBASE + LC823450_MTM_0A)
# define MT00B (LC823450_MTM0_REGBASE + LC823450_MTM_0B)
# define MT00CTL (LC823450_MTM0_REGBASE + LC823450_MTM_0CTL)
# define MT00PSCL (LC823450_MTM0_REGBASE + LC823450_MTM_0PSCL)
# define MT00TIER (LC823450_MTM0_REGBASE + LC823450_MTM_0TIER)
# define MT00OPR (LC823450_MTM0_REGBASE + LC823450_MTM_OPR)
# define MT00CNT (LC823450_MTM0_REGBASE + LC823450_MTM_0CNT)
# define MTM_RELOAD (XT1OSC_CLK / (CLK_TCK * 10))
#endif
#ifdef CONFIG_HRT_TIMER
# define LC823450_MTM2_REGBASE 0x40045000
# define MT20STS (LC823450_MTM2_REGBASE + LC823450_MTM_0STS)
# define MT20A (LC823450_MTM2_REGBASE + LC823450_MTM_0A)
# define MT20PSCL (LC823450_MTM2_REGBASE + LC823450_MTM_0PSCL)
# define MT20TIER (LC823450_MTM2_REGBASE + LC823450_MTM_0TIER)
# define MT2OPR (LC823450_MTM2_REGBASE + LC823450_MTM_OPR)
# define MT20CNT (LC823450_MTM2_REGBASE + LC823450_MTM_0CNT)
#endif /* CONFIG_HRT_TIMER */
#ifdef CONFIG_PROFILE
# define LC823450_MTM3_REGBASE 0x40046000
# define MT30STS (LC823450_MTM3_REGBASE + LC823450_MTM_0STS)
# define MT30A (LC823450_MTM3_REGBASE + LC823450_MTM_0A)
# define MT30B (LC823450_MTM3_REGBASE + LC823450_MTM_0B)
# define MT30CTL (LC823450_MTM3_REGBASE + LC823450_MTM_0CTL)
# define MT30PSCL (LC823450_MTM3_REGBASE + LC823450_MTM_0PSCL)
# define MT30TIER (LC823450_MTM3_REGBASE + LC823450_MTM_0TIER)
# define MT30OPR (LC823450_MTM3_REGBASE + LC823450_MTM_OPR)
# define MT30CNT (LC823450_MTM3_REGBASE + LC823450_MTM_0CNT)
#endif /* CONFIG_PROFILE */
#ifdef CONFIG_DVFS
# define MT01STS (LC823450_MTM0_REGBASE + LC823450_MTM_1STS)
# define MT01A (LC823450_MTM0_REGBASE + LC823450_MTM_1A)
# define MT01B (LC823450_MTM0_REGBASE + LC823450_MTM_1B)
# define MT01CTL (LC823450_MTM0_REGBASE + LC823450_MTM_1CTL)
# define MT01PSCL (LC823450_MTM0_REGBASE + LC823450_MTM_1PSCL)
# define MT01TIER (LC823450_MTM0_REGBASE + LC823450_MTM_1TIER)
# define MT01CNT (LC823450_MTM0_REGBASE + LC823450_MTM_1CNT)
# define MT0OPR (LC823450_MTM0_REGBASE + LC823450_MTM_OPR)
#endif
#ifndef container_of
# define container_of(ptr, type, member) \
((type *)((void *)(ptr) - offsetof(type, member)))
#endif
/****************************************************************************
* Private Types
****************************************************************************/
#ifdef CONFIG_HRT_TIMER
struct hrt_s
{
dq_entry_t ent;
sem_t sem;
int usec;
};
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_HRT_TIMER
static dq_queue_t hrt_timer_queue;
static void hrt_queue_refresh(void);
static void hrt_usleep_setup(void);
static int hrt_interrupt(int irq, FAR void *context, FAR void *arg);
static void hrt_usleep_add(struct hrt_s *phrt);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CHECK_INTERVAL
static bool _timer_val = true;
#endif
#ifdef CONFIG_PROFILE
static int dbg;
#endif
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef CONFIG_PROFILE
uint32_t profile_data[CONFIG_PROFILE_SAMPLES];
int profile_ptr;
int profile_en;
#endif /* CONFIG_PROFILE */
#ifdef CONFIG_HRT_TIMER
extern int g_taskid_init;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: hrt_queue_refresh
****************************************************************************/
#ifdef CONFIG_HRT_TIMER
static void hrt_queue_refresh(void)
{
int elapsed;
dq_entry_t *pent;
struct hrt_s *tmp;
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
elapsed = (uint64_t)getreg32(MT20CNT) * (1000 * 1000) * 10 / XT1OSC_CLK;
for (pent = hrt_timer_queue.head; pent; pent = dq_next(pent))
{
tmp = container_of(pent, struct hrt_s, ent);
tmp->usec -= elapsed;
}
cont:
/* serch for expired */
for (pent = hrt_timer_queue.head; pent; pent = dq_next(pent))
{
tmp = container_of(pent, struct hrt_s, ent);
if (tmp->usec <= 0)
{
dq_rem(pent, &hrt_timer_queue);
spin_unlock_irqrestore(NULL, flags);
nxsem_post(&tmp->sem);
flags = spin_lock_irqsave(NULL);
goto cont;
}
else
{
break;
}
}
spin_unlock_irqrestore(NULL, flags);
}
#endif
/****************************************************************************
* Name: hrt_usleep_setup
****************************************************************************/
#ifdef CONFIG_HRT_TIMER
static void hrt_usleep_setup(void)
{
uint32_t count;
struct hrt_s *head;
irqstate_t flags;
flags = spin_lock_irqsave(NULL);
head = container_of(hrt_timer_queue.head, struct hrt_s, ent);
if (head == NULL)
{
/* MTM2: disable clocking */
modifyreg32(MCLKCNTEXT1, MCLKCNTEXT1_MTM2C_CLKEN, 0x0);
modifyreg32(MCLKCNTEXT1, MCLKCNTEXT1_MTM2_CLKEN, 0x0);
spin_unlock_irqrestore(NULL, flags);
return;
}
/* MTM2: enable clocking */
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM2_CLKEN);
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM2C_CLKEN);
count = (uint64_t)XT1OSC_CLK * head->usec / (1000 * 1000) / 10;
if (count >= 0x8000)
{
count = 0x7fff;
}
putreg32(0, MT20CNT); /* counter */
putreg32(count, MT20A); /* AEVT counter */
/* Enable MTM2-Ch0 */
putreg32(1, MT2OPR);
spin_unlock_irqrestore(NULL, flags);
}
#endif
/****************************************************************************
* Name: hrt_interrupt
****************************************************************************/
#ifdef CONFIG_HRT_TIMER
static int hrt_interrupt(int irq, FAR void *context, FAR void *arg)
{
/* Disable MTM2-Ch0 */
putreg32(0, MT2OPR);
/* clear AEVT Interrupt */
putreg32(1 << 0, MT20STS);
hrt_queue_refresh();
hrt_usleep_setup();
return OK;
}
/****************************************************************************
* Name: hrt_usleep_add
****************************************************************************/
static void hrt_usleep_add(struct hrt_s *phrt)
{
dq_entry_t *pent;
irqstate_t flags;
/* Disable MTM2-Ch0 */
putreg32(0, MT2OPR);
hrt_queue_refresh();
flags = spin_lock_irqsave(NULL);
/* add phrt to hrt_timer_queue */
for (pent = hrt_timer_queue.head; pent; pent = dq_next(pent))
{
struct hrt_s *tmp = container_of(pent, struct hrt_s, ent);
if (tmp->usec >= phrt->usec)
{
break;
}
}
if (pent)
{
dq_addbefore(pent, &phrt->ent, &hrt_timer_queue);
}
else
{
dq_addlast(&phrt->ent, &hrt_timer_queue);
}
spin_unlock_irqrestore(NULL, flags);
hrt_usleep_setup();
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_proftimerisr
****************************************************************************/
#ifdef CONFIG_PROFILE
int up_proftimerisr(int irq, uint32_t *regs, FAR void *arg)
{
putreg32(1 << 1, MT30STS);
if (profile_en)
{
if (profile_ptr != CONFIG_PROFILE_SAMPLES)
{
DEBUGASSERT(current_regs);
profile_data[profile_ptr++] = current_regs[REG_R15];
}
else
{
profile_en = 0;
tmrinfo("PROFILING DONE\n");
}
}
return 0;
}
#endif /* CONFIG_PROFILE */
/****************************************************************************
* Name: up_timerisr
****************************************************************************/
int up_timerisr(int irq, uint32_t *regs, FAR void *arg)
{
/* Process timer interrupt */
#ifdef CONFIG_DVFS
lc823450_dvfs_tick_callback();
#endif
#ifdef CONFIG_LC823450_MTM0_TICK
/* Clear the interrupt (BEVT) */
putreg32(1 << 1, MT00STS);
#endif
nxsched_process_timer();
#ifdef CONFIG_LCA_SOUNDSKIP_CHECK
extern void lca_check_soundskip(void);
lca_check_soundskip();
#endif
#ifdef CHECK_INTERVAL
_timer_val = !_timer_val;
lc823450_gpio_write(TIMER_PIN, _timer_val);
#endif
#ifdef CONFIG_HSUART
hsuart_wdtimer();
#endif /* CONFIG_HSUART */
return 0;
}
/****************************************************************************
* Name: up_hrttimer_usleep
****************************************************************************/
#ifdef CONFIG_HRT_TIMER
int up_hrttimer_usleep(unsigned int usec)
{
struct hrt_s hrt;
nxsem_init(&hrt.sem, 0, 0);
hrt.usec = usec;
hrt_usleep_add(&hrt);
nxsem_wait(&hrt.sem);
return 0;
}
#endif /* CONFIG_HRT_TIMER */
/****************************************************************************
* Name: up_get_timer_fraction
****************************************************************************/
static uint64_t up_get_timer_fraction(void)
{
#ifdef CONFIG_LC823450_MTM0_TICK
uint32_t regval;
uint64_t nsec;
/* read the counter */
regval = getreg32(MT00CNT);
/* check if the timer interrupt is underway */
if (getreg32(MT00STS) & 0x2 && regval < (MTM_RELOAD / 10))
{
return NSEC_PER_TICK;
}
nsec = ((uint64_t)regval * NSEC_PER_TICK / MTM_RELOAD);
return nsec;
#else
uint32_t cur;
uint64_t nsec;
/* read the counter */
cur = getreg32(NVIC_SYSTICK_CURRENT);
/* check if the systick interrupt is pending or active */
if ((getreg32(0xe000ed04) & (1 << 26) ||
getreg32(0xe000ed24) & (1 << 11))
&& (SYSTICK_RELOAD - cur) < (SYSTICK_RELOAD / 10))
{
return NSEC_PER_TICK;
}
nsec = ((uint64_t)(SYSTICK_RELOAD - cur) * NSEC_PER_TICK / SYSTICK_RELOAD);
return nsec;
#endif
}
/****************************************************************************
* Name: up_timer_initialize
****************************************************************************/
void up_timer_initialize(void)
{
#ifdef CHECK_INTERVAL
lc823450_gpio_config(TIMER_PIN |
GPIO_MODE_OUTPUT |
GPIO_VALUE_ONE);
#endif
#ifdef CONFIG_HRT_TIMER
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM2_CLKEN);
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM2C_CLKEN);
/* MTM2: unreset */
modifyreg32(MRSTCNTEXT1, 0x0, MRSTCNTEXT1_MTM2_RSTB);
/* Enable AEVT Interrupt */
putreg32(1 << 0, MT20TIER);
/* Set prescaler to (1/10) */
putreg32(10 - 1, MT20PSCL);
modifyreg32(MCLKCNTEXT1, MCLKCNTEXT1_MTM2C_CLKEN, 0);
modifyreg32(MCLKCNTEXT1, MCLKCNTEXT1_MTM2_CLKEN, 0);
irq_attach(LC823450_IRQ_MTIMER20, (xcpt_t)hrt_interrupt, NULL);
up_enable_irq(LC823450_IRQ_MTIMER20);
#endif /* CONFIG_HRT_TIMER */
#ifdef CONFIG_PROFILE
/* MTM3: enable clocking */
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM3_CLKEN);
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM3C_CLKEN);
/* MTM3: unreset */
modifyreg32(MRSTCNTEXT1, 0x0, MRSTCNTEXT1_MTM3_RSTB);
/* Input clock for the MTM3 is XT1 (i.e. 24M or 20M)
* then the clock will be set to 1/10 by the internal divider
* To implement 10ms timer, ADT=0, BDT=MTM_RELOAD
*/
putreg32(0, MT30A); /* AEVT counter */
putreg32((XT1OSC_CLK / 1010) - 1, MT30B); /* BEVT counter */
/* Clear the counter by BEVT */
putreg32(0x80, MT30CTL);
/* Set prescaler to 9 : (1/10) */
putreg32(9, MT30PSCL);
/* Enable BEVT Interrupt */
putreg32(1 << 1, MT30TIER);
/* Enable MTM3-Ch0 */
putreg32(1, MT30OPR);
/* Attach the timer interrupt vector */
irq_attach(LC823450_IRQ_MTIMER30, (xcpt_t)up_proftimerisr, NULL);
/* And enable the system timer interrupt */
up_enable_irq(LC823450_IRQ_MTIMER30);
#endif /* CONFIG_PROFILE */
#ifdef CONFIG_LC823450_MTM0_TICK
/* MTM0: enable clocking */
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM0_CLKEN);
modifyreg32(MCLKCNTEXT1, 0x0, MCLKCNTEXT1_MTM0C_CLKEN);
/* MTM0: unreset */
modifyreg32(MRSTCNTEXT1, 0x0, MRSTCNTEXT1_MTM0_RSTB);
/* Input clock for the MTM0 is XT1 (i.e. 24M or 20M)
* then the clock will be set to 1/10 by the internal divider
* To implement the tick timer, ADT=0, BDT=MTM_RELOAD-1
*/
putreg32(0, MT00A); /* AEVT counter */
putreg32(MTM_RELOAD - 1, MT00B); /* BEVT counter */
/* Clear the counter by BEVT */
putreg32(0x80, MT00CTL);
/* Set prescaler to 9 : (1/10) */
putreg32(9, MT00PSCL);
/* Enable BEVT Interrupt */
putreg32(1 << 1, MT00TIER);
/* Enable MTM0-Ch0 */
putreg32(1, MT00OPR);
/* Attach the timer interrupt vector */
irq_attach(LC823450_IRQ_MTIMER00, (xcpt_t)up_timerisr, NULL);
/* And enable the system timer interrupt */
up_enable_irq(LC823450_IRQ_MTIMER00);
#else
uint32_t regval;
/* Set the SysTick interrupt to the default priority */
regval = getreg32(NVIC_SYSH12_15_PRIORITY);
regval &= ~NVIC_SYSH_PRIORITY_PR15_MASK;
regval |= (NVIC_SYSH_PRIORITY_DEFAULT << NVIC_SYSH_PRIORITY_PR15_SHIFT);
putreg32(regval, NVIC_SYSH12_15_PRIORITY);
/* Make sure that the SYSTICK clock source is set correctly */
#if 0 /* Does not work. Comes up with HCLK source and I can't change it */
regval = getreg32(NVIC_SYSTICK_CTRL);
#if CONFIG_LC823450_SYSTICK_HCLKd8
regval &= ~NVIC_SYSTICK_CTRL_CLKSOURCE;
#else
regval |= NVIC_SYSTICK_CTRL_CLKSOURCE;
#endif
putreg32(regval, NVIC_SYSTICK_CTRL);
#endif
/* Configure SysTick to interrupt at the requested rate */
putreg32(SYSTICK_RELOAD, NVIC_SYSTICK_RELOAD);
/* Attach the timer interrupt vector */
irq_attach(LC823450_IRQ_SYSTICK, (xcpt_t)up_timerisr, NULL);
/* Enable SysTick interrupts */
putreg32((NVIC_SYSTICK_CTRL_CLKSOURCE |
NVIC_SYSTICK_CTRL_TICKINT |
NVIC_SYSTICK_CTRL_ENABLE),
NVIC_SYSTICK_CTRL);
/* And enable the timer interrupt */
up_enable_irq(LC823450_IRQ_SYSTICK);
#endif
#ifdef CONFIG_DVFS
/* attach timer interrupt handler */
irq_attach(LC823450_IRQ_MTIMER01, (xcpt_t)lc823450_dvfs_oneshot, NULL);
/* enable MTM0-ch1 */
up_enable_irq(LC823450_IRQ_MTIMER01);
#endif
}
/****************************************************************************
* Name: lc823450_mtm_start_oneshot
* NOTE: Assumption: MTM0-ch0 is running (i.e. tick timer)
****************************************************************************/
#ifdef CONFIG_DVFS
void lc823450_mtm_start_oneshot(int msec)
{
uint32_t r = MTM_RELOAD; /* 10ms */
r /= 10; /* 1ms */
r *= msec;
putreg32(0, MT01A); /* AEVT counter */
putreg32(r - 1, MT01B); /* BEVT counter */
/* Clear the counter by BEVT */
putreg32(0x80, MT01CTL);
/* Set prescaler to 9 : (1/10) */
putreg32(9, MT01PSCL);
/* Enable BEVT Interrupt */
putreg32(1 << 1, MT01TIER);
/* Enable MTM0-ch1 */
modifyreg32(MT0OPR, 0, 1 << 1);
}
#endif
/****************************************************************************
* Name: lc823450_mtm_stop_oneshot
****************************************************************************/
#ifdef CONFIG_DVFS
void lc823450_mtm_stop_oneshot(void)
{
/* Clear the interrupt (BEVT) */
putreg32(1 << 1, MT01STS);
/* Disable MTM0-ch1 */
modifyreg32(MT0OPR, 1 << 1, 0);
}
#endif
/****************************************************************************
* Name: up_rtc_gettime
*
* Description:
* This function is used in clock_gettime() to obtain high resolution time.
*
****************************************************************************/
int up_rtc_gettime(FAR struct timespec *tp)
{
uint64_t secs;
uint64_t nsecs;
uint64_t elapsed; /* in nsec */
irqstate_t flags;
uint64_t f;
flags = spin_lock_irqsave(NULL);
/* Get the elapsed time */
elapsed = NSEC_PER_TICK * (uint64_t)g_system_timer;
/* Add the tiemr fraction in nanoseconds */
f = up_get_timer_fraction();
elapsed += f;
spin_unlock_irqrestore(NULL, flags);
tmrinfo("elapsed = %lld \n", elapsed);
/* Convert the elapsed time in seconds and nanoseconds. */
secs = elapsed / NSEC_PER_SEC;
nsecs = (elapsed - secs * NSEC_PER_SEC);
/* And return the result to the caller. */
tp->tv_sec = (time_t)secs;
tp->tv_nsec = (long)nsecs;
tmrinfo("Returning tp=(%d,%d)\n", (int)tp->tv_sec, (int)tp->tv_nsec);
return OK;
}