| /* Copyright Statement: |
| * |
| * This software/firmware and related documentation ("MediaTek Software") are |
| * protected under relevant copyright laws. The information contained herein |
| * is confidential and proprietary to MediaTek Inc. and/or its licensors. |
| * Without the prior written permission of MediaTek inc. and/or its licensors, |
| * any reproduction, modification, use or disclosure of MediaTek Software, |
| * and information contained herein, in whole or in part, shall be strictly prohibited. |
| */ |
| /* MediaTek Inc. (C) 2015. All rights reserved. |
| * |
| * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES |
| * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") |
| * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON |
| * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. |
| * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE |
| * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR |
| * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH |
| * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES |
| * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES |
| * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK |
| * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR |
| * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND |
| * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, |
| * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, |
| * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO |
| * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. |
| */ |
| |
| #include <driver_api.h> |
| #include <tinysys_reg.h> |
| #include <interrupt.h> |
| #include <xgpt.h> |
| #include <FreeRTOS.h> |
| #include <semphr.h> |
| #include <FreeRTOSConfig.h> |
| |
| #include <task.h> |
| |
| #ifdef CFG_VCORE_DVFS_SUPPORT |
| #include <dvfs.h> |
| #include <sleep.h> |
| #endif |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "mtk_atomic.h" |
| #include "encoding.h" |
| |
| unsigned int g_timer_base; |
| static struct timer_device scp_timer[NR_TMRS]; |
| static platform_timer_callback tick_timer_callback; |
| static void *tick_timer_callback_arg; |
| |
| #ifdef CFG_CHRE_SUPPORT |
| static platform_timer_callback chre_timer_callback; |
| static void *chre_timer_callback_arg; |
| #endif |
| |
| #define UNUSED(x) (void)(x) |
| |
| // 200MHz setting |
| #define MCLK_RATE 200*1024*1024 //200M |
| #define MCLK_1TICK_NS 5 // 1/(200M) = 5nsec |
| |
| #define SEC_TO_NSEC (1000 * 1000 * 1000) |
| |
| #ifdef CFG_FPGA |
| #define TIMER_TICK_RATE 20480 |
| #else |
| #define TIMER_TICK_RATE 32768 |
| #endif |
| |
| /* multiplier and shifter for 13MHz global timer */ |
| #ifdef CFG_FPGA |
| #define OSTIMER_TIMER_MULT (419430400) /* 5M clk, 1000000000/(5*1000*1000) <<OSTIMER_TIMER_SHIFT */ |
| #else |
| #define OSTIMER_TIMER_MULT (161319385) |
| #endif |
| #define OSTIMER_TIMER_SHIFT (21) |
| |
| #ifdef OSTIMER_LATCH_TIME_SUPPORT |
| void init_latch_time(void); |
| #else |
| void init_latch_time(void) |
| { |
| } |
| #endif |
| |
| struct timer_device *id_to_dev(unsigned int id) |
| { |
| return id < NR_TMRS ? scp_timer + id : 0; |
| } |
| |
| static void __timer_enable_irq(struct timer_device *dev) |
| { |
| DRV_SetReg32(dev->base_addr + TIMER_IRQ_CTRL_REG, 0x1); |
| } |
| |
| static void __timer_disable_irq(struct timer_device *dev) |
| { |
| DRV_ClrReg32(dev->base_addr + TIMER_IRQ_CTRL_REG, 0x1); |
| } |
| |
| static void __timer_ack_irq(struct timer_device *dev) |
| { |
| DRV_SetReg32(dev->base_addr + TIMER_IRQ_CTRL_REG, TIMER_IRQ_CLEAR); |
| } |
| |
| static void __timer_enable(struct timer_device *dev) |
| { |
| DRV_SetReg32(dev->base_addr + TIMER_EN, 0x1); |
| } |
| |
| static void __timer_disable(struct timer_device *dev) |
| { |
| DRV_ClrReg32(dev->base_addr + TIMER_EN, 0x1); |
| } |
| |
| static void __timer_set_clk(struct timer_device *dev, unsigned int clksrc) |
| { |
| DRV_ClrReg32(dev->base_addr + TIMER_CLK_SRC, |
| TIMER_CLK_SRC_MASK << TIMER_CLK_SRC_SHIFT); |
| DRV_SetReg32(dev->base_addr + TIMER_CLK_SRC, |
| clksrc << TIMER_CLK_SRC_SHIFT); |
| } |
| |
| static void __timer_set_rstval(struct timer_device *dev, unsigned int val) |
| { |
| DRV_WriteReg32(dev->base_addr + TIMER_RST_VAL, val); |
| } |
| |
| static void __timer_get_curval(struct timer_device *dev, unsigned long *ptr) |
| { |
| *ptr = DRV_Reg32(dev->base_addr + TIMER_CUR_VAL_REG); |
| } |
| |
| static void __timer_reset(struct timer_device *dev) |
| { |
| __timer_disable(dev); |
| __timer_disable_irq(dev); |
| __timer_ack_irq(dev); |
| __timer_set_rstval(dev, 0); |
| __timer_set_clk(dev, TIMER_CLK_SRC_CLK_32K); |
| } |
| |
| #if defined(CFG_MED_MCU_DVT) |
| struct timer_device *get_dev_by_id(unsigned int id) |
| { |
| return id_to_dev(id); |
| } |
| |
| void timer_enable_irq(struct timer_device *dev) |
| { |
| __timer_enable_irq(dev); |
| } |
| |
| void timer_enable_set(struct timer_device *dev) |
| { |
| __timer_enable(dev); |
| } |
| |
| void timer_disable_set(struct timer_device *dev) |
| { |
| __timer_disable(dev); |
| } |
| |
| void timer_set_clk(struct timer_device *dev, unsigned int clksrc) |
| { |
| __timer_set_clk(dev, clksrc); |
| } |
| |
| void timer_set_rstval(struct timer_device *dev, unsigned int val) |
| { |
| __timer_set_rstval(dev, val); |
| } |
| |
| unsigned long timer_get_curval(struct timer_device *dev) |
| { |
| unsigned long val; |
| |
| __timer_get_curval(dev, &val); |
| |
| return val; |
| } |
| #endif |
| |
| /* get ostimer counter */ |
| unsigned long long timer_get_global_timer_tick(void) |
| { |
| unsigned long long val = 0; |
| unsigned long high = 0, low = 0; |
| unsigned long long new_high = 0, new_low = 0; |
| |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| |
| low = DRV_Reg32(OSTIMER_CUR_L); |
| high = DRV_Reg32(OSTIMER_CUR_H); |
| |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| |
| new_high = high; |
| new_low = low; |
| val = |
| ((new_high << 32) & 0xFFFFFFFF00000000ULL) | (new_low & |
| 0x00000000FFFFFFFFULL); |
| |
| return val; |
| } |
| |
| static SYNC_SECTION unsigned long long ostimer_init_cycle; |
| static unsigned long long ostimer_last_cycle; |
| static unsigned int delay_get_current_tick(void); |
| |
| /* get ostimer timestamp */ |
| unsigned long long get_boot_time_ns(void) |
| { |
| unsigned long long high = 0, low = 0; |
| unsigned long long cycle = 0; |
| unsigned long long timestamp = 0; |
| |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| |
| #ifdef CFG_USE_32K_HW_TIMER |
| low = DELAY_TIMER_RSTVAL - delay_get_current_tick(); |
| #else |
| low = DRV_Reg32(OSTIMER_CUR_L); |
| high = DRV_Reg32(OSTIMER_CUR_H); |
| #endif |
| |
| cycle = ((high << 32) & 0xFFFFFFFF00000000ULL) | |
| (low & 0x00000000FFFFFFFFULL); |
| |
| #ifndef CFG_USE_32K_HW_TIMER |
| if (cycle < ostimer_last_cycle) |
| cycle = ostimer_last_cycle + 1; |
| #endif |
| ostimer_last_cycle = cycle; |
| |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| |
| #ifndef CFG_USE_32K_HW_TIMER |
| cycle = cycle - ostimer_init_cycle; |
| high = (cycle >> 32) & 0x00000000FFFFFFFFULL; |
| #endif |
| low = cycle & 0x00000000FFFFFFFFULL; |
| |
| #ifdef CFG_USE_32K_HW_TIMER |
| timestamp = (cycle * 1000000000) >> 15; |
| #else |
| timestamp = |
| (((unsigned long long) high * OSTIMER_TIMER_MULT) << 11) + |
| (((unsigned long long) low * |
| OSTIMER_TIMER_MULT) >> OSTIMER_TIMER_SHIFT); |
| #endif |
| |
| return timestamp; |
| } |
| |
| unsigned long long read_xgpt_stamp_ns(void) |
| { |
| return get_boot_time_ns(); |
| } |
| |
| static void tick_timer_irq_handle(void); |
| static void __clr_all_tmr_irq_ack(void); |
| void mt_platform_timer_init(void) |
| { |
| int i; |
| struct timer_device *dev; |
| g_timer_base = XGPT_BASE_REG; |
| /* enable clock */ |
| DRV_SetReg32(TIMER_IN_CLK, (TMR_MCLK_CG | TMR_BCLK_CG)); |
| |
| for (i = 0; i < NR_TMRS; i++) { |
| scp_timer[i].id = i; |
| scp_timer[i].base_addr = SCP_TIMER_BASE + 0x10 * i; |
| } |
| |
| scp_timer[0].irq = INTC_IRQ_XGPT0; |
| scp_timer[1].irq = INTC_IRQ_XGPT1; |
| scp_timer[2].irq = INTC_IRQ_XGPT2; |
| scp_timer[3].irq = INTC_IRQ_XGPT3; |
| scp_timer[4].irq = INTC_IRQ_XGPT4; |
| scp_timer[5].irq = INTC_IRQ_XGPT5; |
| |
| __clr_all_tmr_irq_ack(); |
| |
| // reset timer |
| for (i = 0; i < NR_TMRS; i++) { |
| __timer_reset(&scp_timer[i]); |
| } |
| |
| /* Setup timer to wakeup source for tickless mode to wakeup device. */ |
| for (i = 0; i < NR_TMRS; i++) { |
| intc_irq_wakeup_set(&scp_timer[i].irq, 0x1); |
| } |
| |
| /* enable delay GPT */ |
| dev = id_to_dev(DELAY_TIMER); |
| __timer_set_rstval(dev, DELAY_TIMER_RSTVAL); |
| __timer_enable(dev); |
| |
| mdelay(1); |
| |
| dev = id_to_dev(TICK_TIMER); |
| intc_irq_request(&dev->irq, (void *) tick_timer_irq_handle, (void *) 0); |
| |
| if (mrv_read_csr(CSR_MHARTID) == 0) { |
| ostimer_init_cycle = timer_get_global_timer_tick(); |
| printf("[ostimer] ostimer_init_cycle = %llu cycles\n", |
| ostimer_init_cycle); |
| } |
| init_latch_time(); |
| } |
| |
| static void tick_timer_irq(void *arg) |
| { |
| UNUSED(arg); |
| |
| /*diff with before */ |
| if (tick_timer_callback != NULL) |
| return tick_timer_callback(tick_timer_callback_arg); |
| } |
| |
| static void tick_timer_irq_handle(void) |
| { |
| struct timer_device *dev = id_to_dev(TICK_TIMER); |
| |
| __timer_disable(dev); |
| __timer_disable_irq(dev); |
| __timer_ack_irq(dev); |
| tick_timer_irq(0); |
| |
| return; |
| } |
| |
| int platform_set_periodic_timer(platform_timer_callback callback, void *arg, |
| mt_time_t interval) |
| { |
| struct timer_device *dev; |
| unsigned long long tmp_64 = 0; |
| unsigned long long interval_tmp = (unsigned long long) interval; |
| |
| tick_timer_callback = callback; |
| tick_timer_callback_arg = arg; |
| |
| tmp_64 = (unsigned long long) TIMER_TICK_RATE *interval_tmp; |
| tmp_64 = (tmp_64 / 1000ULL - 32); |
| dev = id_to_dev(TICK_TIMER); |
| __timer_disable(dev); |
| |
| if (interval >= 1) |
| __timer_set_rstval(dev, (unsigned int) tmp_64); //0.3ms(sw)+0.7ms(hw wake) |
| else |
| __timer_set_rstval(dev, 1); |
| __timer_enable_irq(dev); |
| |
| __timer_enable(dev); |
| |
| return 0; |
| } |
| |
| #ifdef CFG_CHRE_SUPPORT |
| static void chre_timer_stop(void) |
| { |
| struct timer_device *dev = id_to_dev(CHRE_TIMER); |
| |
| __timer_disable(dev); |
| } |
| |
| static void chre_timer_irq(void *arg) |
| { |
| (void)*arg; |
| if (chre_timer_callback != NULL) |
| return chre_timer_callback(chre_timer_callback_arg); |
| } |
| |
| static unsigned int chre_timer_irq_handle(void *arg) |
| { |
| struct timer_device *dev = id_to_dev(CHRE_TIMER); |
| __timer_disable(dev); |
| __timer_disable_irq(dev); |
| __timer_ack_irq(dev); |
| chre_timer_irq(0); |
| |
| return 0; |
| } |
| |
| static int platform_set_periodic_timer_chre(platform_timer_callback callback, |
| void *arg, mt_time_t interval_ns) |
| { |
| struct timer_device *dev; |
| unsigned long long interval_tick = 0; |
| |
| chre_timer_callback = callback; |
| chre_timer_callback_arg = arg; |
| |
| /* calculate how many ticks shall we wait */ |
| interval_tick = |
| (interval_ns / 1000) * (unsigned long long) TIMER_TICK_RATE / |
| 1000000ULL; |
| |
| dev = id_to_dev(CHRE_TIMER); |
| |
| /* setup 1-tick timer if required tick is < 1 */ |
| if (interval_tick >= 1) |
| __timer_set_rstval(dev, (unsigned int) interval_tick); |
| else |
| __timer_set_rstval(dev, 1); |
| |
| __timer_enable_irq(dev); |
| |
| intc_irq_request(&dev->irq, chre_timer_irq_handle, (void *)0); |
| |
| __timer_enable(dev); |
| |
| return 0; |
| } |
| #endif |
| #if 1 // (RV only) |
| void timer_cpu_tick_irq_ack(void) |
| { |
| DRV_WriteReg32(TIMER_CPU_TICK_IRQ_CTRL, TIMERCPU_TICK_IRQ_CLR); |
| } |
| |
| void platform_cpu_tick_disable(void) |
| { |
| DRV_ClrReg32(GENERAL_CTRL, B_CPU_TIMER_INT_EN); |
| } |
| |
| void platform_cpu_tick_enable(void) |
| { |
| DRV_SetReg32(GENERAL_CTRL, B_CPU_TIMER_INT_EN); |
| } |
| |
| /* interval: timer for systick clock */ |
| void platform_set_cpu_tick(int interval) |
| { |
| DRV_WriteReg32(TIMER_CPU_TICK_RST_VAL, interval); |
| DRV_WriteReg32(TIMER_CPU_TICK_IRQ_CTRL, |
| TIMERCPU_TICK_IRQ_CLR | TIMER_CPU_TICK_IRQ_EN); |
| DRV_WriteReg32(TIMER_CPU_TICK_EN, 1); |
| } |
| |
| static void __clr_all_tmr_irq_ack(void) |
| { |
| /* clr tmr0~5 */ |
| __timer_ack_irq(id_to_dev(TMR0)); |
| __timer_ack_irq(id_to_dev(TMR1)); |
| __timer_ack_irq(id_to_dev(TMR2)); |
| __timer_ack_irq(id_to_dev(TMR3)); |
| __timer_ack_irq(id_to_dev(TMR4)); |
| __timer_ack_irq(id_to_dev(TMR5)); |
| /* clr cpu tick */ |
| timer_cpu_tick_irq_ack(); |
| /* clr os timer */ |
| DRV_WriteReg32(OSTIMER_IRQ_ACK, OSTIMER_TICK_IRQ_CLR); |
| } |
| #endif |
| |
| static unsigned int delay_get_current_tick(void) |
| { |
| unsigned long current_count; |
| struct timer_device *dev = id_to_dev(DELAY_TIMER); |
| |
| __timer_get_curval(dev, ¤t_count); |
| return current_count; |
| } |
| |
| static int check_timeout_tick(unsigned int start_tick, |
| unsigned int timeout_tick) |
| { |
| //register unsigned int cur_tick; |
| //register unsigned int elapse_tick; |
| unsigned int cur_tick; |
| unsigned int elapse_tick; |
| // get current tick |
| cur_tick = delay_get_current_tick(); |
| |
| // check elapse time, down counter |
| if (start_tick >= cur_tick) { |
| elapse_tick = start_tick - cur_tick; |
| } else { |
| elapse_tick = (DELAY_TIMER_RSTVAL - cur_tick) + start_tick; |
| } |
| // check if timeout |
| if (timeout_tick <= elapse_tick) { |
| // timeout |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static unsigned int time2tick_us(unsigned int time_us) |
| { |
| return TIME_TO_TICK_US(time_us); |
| } |
| |
| static unsigned int time2tick_ms(unsigned int time_ms) |
| { |
| return TIME_TO_TICK_MS(time_ms); |
| } |
| |
| //=========================================================================== |
| // busy wait |
| //=========================================================================== |
| static void busy_wait_us(unsigned int timeout_us) |
| { |
| unsigned int start_tick, timeout_tick; |
| |
| // get timeout tick |
| timeout_tick = time2tick_us(timeout_us); |
| start_tick = delay_get_current_tick(); |
| |
| // wait for timeout |
| while (!check_timeout_tick(start_tick, timeout_tick)) ; |
| } |
| |
| static void busy_wait_ms(unsigned int timeout_ms) |
| { |
| unsigned int start_tick, timeout_tick; |
| |
| // get timeout tick |
| timeout_tick = time2tick_ms(timeout_ms); |
| start_tick = delay_get_current_tick(); |
| |
| // wait for timeout |
| while (!check_timeout_tick(start_tick, timeout_tick)) ; |
| } |
| |
| /* delay msec mseconds */ |
| void mdelay(unsigned long msec) |
| { |
| busy_wait_ms(msec); |
| } |
| |
| /* delay usec useconds */ |
| void udelay(unsigned long usec) |
| { |
| unsigned long usec_t; |
| if (usec < US_LIMIT) { |
| //PRINTF_D("usec < 31us, error parameter\n"); |
| busy_wait_us(1); |
| } else { |
| usec_t = usec / 31 + 1; |
| busy_wait_us(usec_t); |
| } |
| } |
| |
| #ifdef CFG_CHRE_SUPPORT |
| //=========================================================================== |
| // chre feature |
| //=========================================================================== |
| extern uint64_t cpuIntsOff(void); |
| extern void cpuIntsRestore(uint64_t state); |
| extern TaskHandle_t CHRE_TaskHandle; |
| |
| bool platSleepClockRequest(uint64_t wakeupTime, uint32_t maxJitterPpm, |
| uint32_t maxDriftPpm, uint32_t maxErrTotalPpm); |
| static void chre_timer_wakeup(void *arg) |
| { |
| extern int timIntHandler(void); |
| timIntHandler(); |
| if (xTaskResumeFromISR(CHRE_TaskHandle) == pdTRUE) |
| portYIELD_WITHIN_API(); |
| } |
| |
| bool platSleepClockRequest(uint64_t wakeupTime, uint32_t maxJitterPpm, |
| uint32_t maxDriftPpm, uint32_t maxErrTotalPpm) |
| { |
| (void)maxJitterPpm; |
| (void)maxDriftPpm; |
| (void)maxErrTotalPpm; |
| // uint64_t intState, curTime; |
| uint64_t curTime; |
| unsigned long long diff_time; |
| chre_timer_stop(); |
| if (wakeupTime == 0) { |
| return 1; |
| } |
| curTime = (uint64_t) read_xgpt_stamp_ns(); |
| |
| if (wakeupTime && curTime >= wakeupTime) |
| return 0; |
| |
| /* diff_time unit: ns */ |
| diff_time = wakeupTime - curTime; |
| |
| // intState = cpuIntsOff(); |
| // TODO: set an actual alarm here so that if we keep running and do not sleep till this is due, |
| // we still fire an interrupt for it! |
| platform_set_periodic_timer_chre(chre_timer_wakeup, NULL, diff_time); |
| // cpuIntsRestore(intState); |
| return 1; |
| } |
| #endif |
| |
| void (*rttimercbk)(void); |
| |
| static unsigned int rt_timer_irq_handler(void *arg) |
| { |
| struct timer_device *dev = id_to_dev(RT_TIMER); |
| __timer_disable(dev); |
| __timer_disable_irq(dev); |
| __timer_ack_irq(dev); |
| if (rttimercbk != NULL) |
| rttimercbk(); |
| return 0; |
| } |
| |
| unsigned int program_hwtimer(void (*callback)(void), uint64_t diff_time) |
| { |
| struct timer_device *dev = id_to_dev(RT_TIMER); |
| unsigned long long interval_tick = 0; |
| |
| __timer_disable(dev); |
| |
| if (diff_time == 0) { |
| return 1; |
| } |
| /* calculate how many ticks shall we wait */ |
| interval_tick = |
| (diff_time / 1000) * (unsigned long long)TIMER_TICK_RATE / |
| 1000000ULL; |
| |
| /* setup 1-tick timer if required tick is < 1 */ |
| if (interval_tick >= 1) |
| __timer_set_rstval(dev, (unsigned int)interval_tick); |
| else |
| __timer_set_rstval(dev, 1); |
| |
| __timer_enable_irq(dev); |
| |
| rttimercbk = callback; |
| |
| intc_irq_request(&dev->irq, rt_timer_irq_handler, (void *)0); |
| |
| __timer_enable(dev); |
| |
| return 1; |
| } |
| |
| #ifdef OSTIMER_LATCH_TIME_SUPPORT |
| #define MAX_LATCH_TIMER 3 |
| #define CRTL_BIT_SHIFT 8 |
| #define IRQ_BIT_SHIFT 0 |
| #define ENABLE_BIT_SHIFT 5 |
| #define CYC_BASE_SHIFT 8 |
| struct latch_time_struct { |
| unsigned int ctrl_base; |
| unsigned int enable_offset; |
| unsigned int irq_offset; |
| unsigned int cyc_low_base; |
| unsigned int cyc_high_base; |
| }; |
| static struct latch_time_struct latch_time[MAX_LATCH_TIMER]; |
| static unsigned int latch_time_used[MAX_LATCH_TIMER]; |
| void init_latch_time(void) |
| { |
| int i = 0; |
| for (i = 0; i < MAX_LATCH_TIMER; ++i) { |
| latch_time[i].ctrl_base = OS_TIMER_LATCH_CTRL; |
| latch_time[i].enable_offset = |
| i * CRTL_BIT_SHIFT + ENABLE_BIT_SHIFT; |
| latch_time[i].irq_offset = i * CRTL_BIT_SHIFT + IRQ_BIT_SHIFT; |
| latch_time[i].cyc_low_base = |
| OS_TIMER_LATCH_VALUE_0 + i * CYC_BASE_SHIFT; |
| latch_time[i].cyc_high_base = latch_time[i].cyc_low_base + 4; |
| |
| latch_time_used[i] = 0; |
| } |
| DRV_WriteReg32(OS_TIMER_LATCH_CTRL, 0); |
| } |
| |
| void enable_latch_time(int id, int irq) |
| { |
| unsigned int base = 0, control = 0; |
| |
| if (id < 0 || id >= MAX_LATCH_TIMER) |
| return; |
| if (irq > 0x1f) |
| return; |
| base = latch_time[id].ctrl_base; |
| control = ((1 << latch_time[id].enable_offset) | |
| (irq << latch_time[id].irq_offset)); |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| DRV_SetReg32(base, control); |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| } |
| |
| void disable_latch_time(int id) |
| { |
| unsigned int base = 0, control = 0; |
| |
| if (id < 0 || id >= MAX_LATCH_TIMER) |
| return; |
| base = latch_time[id].ctrl_base; |
| control = ((0 << latch_time[id].enable_offset) | |
| (0 << latch_time[id].irq_offset)); |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| DRV_SetReg32(base, control); |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| } |
| |
| int alloc_latch_time(void) |
| { |
| int i = 0, id = -1; |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| for (i = 0; i < MAX_LATCH_TIMER; ++i) { |
| if (latch_time_used[i] == 0) { |
| latch_time_used[i] = 1; |
| id = i; |
| break; |
| } |
| } |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| return id; |
| } |
| |
| void free_latch_time(int id) |
| { |
| if (id < 0 || id >= MAX_LATCH_TIMER) |
| return; |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| latch_time_used[id] = 0; |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| } |
| |
| static unsigned long long get_latch_time_counter(struct latch_time_struct *base) |
| { |
| unsigned long long val; |
| unsigned long high_1, high_2, low_1, low_2; |
| |
| if (!is_in_isr()) |
| taskENTER_CRITICAL(); |
| |
| low_1 = DRV_Reg32(base->cyc_low_base); |
| high_1 = DRV_Reg32(base->cyc_high_base); |
| low_2 = DRV_Reg32(base->cyc_low_base); |
| high_2 = DRV_Reg32(base->cyc_high_base); |
| |
| if (low_2 < low_1) { |
| high_1 = high_2; |
| low_1 = low_2; |
| } |
| |
| val = (((unsigned long long) high_1 << 32) & 0xFFFFFFFF00000000) | |
| ((unsigned long long) low_1 & 0x00000000FFFFFFFF); |
| |
| if (!is_in_isr()) |
| taskEXIT_CRITICAL(); |
| |
| return val; |
| } |
| |
| uint64_t get_latch_time_timestamp(int id) |
| { |
| unsigned long long cycle = 0; |
| unsigned long long high = 0, low = 0; |
| uint64_t timestamp = 0; |
| |
| if (id < 0 || id >= MAX_LATCH_TIMER) |
| return 0; |
| cycle = get_latch_time_counter(&latch_time[id]); |
| |
| cycle = cycle - ostimer_init_cycle; |
| high = (cycle >> 32) & 0x00000000FFFFFFFFULL; |
| low = cycle & 0x00000000FFFFFFFFULL; |
| timestamp = (((unsigned long long) high * OSTIMER_TIMER_MULT) << 11) + |
| (((unsigned long long) low * |
| OSTIMER_TIMER_MULT) >> OSTIMER_TIMER_SHIFT); |
| return timestamp; |
| } |
| #endif |