/**************************************************************************** * 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 #include #include #include #include #include #include #include #include #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; }