/** @file wlan_axi_timer.c * * Wrapper for axi_timer peripheral * * @copyright Copyright 2013-2019, Mango Communications. All rights reserved. * Distributed under the Mango Communications Reference Design License * See LICENSE.txt included in the design archive or * at http://mangocomm.com/802.11/license * * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) */ #include "wlan_mac_common.h" #include "wlan_mac_high.h" #include "wlan_platform_common.h" #include "wlan_platform_intc.h" #include "wlan_platform_timer.h" #include "xtmrctr.h" #define TIMER_NUMBER 0 // currently only timer #0 is used void timer_isr(void* instance_ptr); int timer_null_callback(); static XTmrCtr timer_instance; static u32 timer_interval; function_ptr_t timer_callback; /*****************************************************************************/ /** * Timer initialization * * @param timer_dev_id - Timer peripheral device ID, from xparameters * @param interval - Timer interval in clock ticks * @param callback - Function timer ISR should call on every interrupt * * @return status - WLAN_SUCCESS or WLAN_FAILURE * *****************************************************************************/ int wlan_timer_init(int timer_dev_id, u32 interval) { // Initialize the timer // The driver for the timer does not handle a reboot of CPU_HIGH // gracefully. Before initializing it, we should stop any running // timers. Note: we cannot use the official XTmrCtr_Stop() function // for this since it requires that the timer instance be initialized. int status; u32 csr_reg; XTmrCtr_Config *TmrCtrConfigPtr = XTmrCtr_LookupConfig(timer_dev_id); // Assign a safe do-nothing callback, catches unlikely case of this init() failing below // but an interrupt still occurring later timer_callback = timer_null_callback; if(interval < 100) { xil_printf("ERROR (wlan_timer_init): interval %d too small, must be >100\n", interval); return WLAN_FAILURE; } timer_interval = interval; // Disable both timers in the core if(TmrCtrConfigPtr != NULL) { csr_reg = XTmrCtr_ReadReg(TmrCtrConfigPtr->BaseAddress, 0, XTC_TCSR_OFFSET); csr_reg &= ~(XTC_CSR_ENABLE_TMR_MASK); XTmrCtr_WriteReg(TmrCtrConfigPtr->BaseAddress, 0, XTC_TCSR_OFFSET, csr_reg); csr_reg = XTmrCtr_ReadReg(TmrCtrConfigPtr->BaseAddress, 1, XTC_TCSR_OFFSET); csr_reg &= ~(XTC_CSR_ENABLE_TMR_MASK); XTmrCtr_WriteReg(TmrCtrConfigPtr->BaseAddress, 1, XTC_TCSR_OFFSET, csr_reg); status = XTmrCtr_Initialize(&timer_instance, timer_dev_id); if (status != XST_SUCCESS) { xil_printf("ERROR: XTmrCtr_Initialize failed with status %d\n", status); return WLAN_FAILURE; } // Configure timer 0 for use by the platform // Timer counts down from the software-set value, interrupt occurs at 0 // Timer is automatically reloaded when it wraps, software only needs to acknowledge the interrupt XTmrCtr_SetResetValue(&timer_instance, TIMER_NUMBER, timer_interval); XTmrCtr_SetOptions(&timer_instance, TIMER_NUMBER, XTC_DOWN_COUNT_OPTION | XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION); return WLAN_SUCCESS; } else { //Could not find XTmrCtr_Config for the timer xil_printf("ERROR: timer init failed - no config struct found by XTmrCtr_LookupConfig()\n"); return WLAN_FAILURE; } } /*****************************************************************************/ /** * Sets up timer interrupt * * @param int_id - Interrupt ID for timer peripheral, from xparameters * * @return status - WLAN_SUCCESS or WLAN_FAILURE * *****************************************************************************/ int wlan_timer_setup_interrupt(int int_id, function_ptr_t callback) { int status; timer_callback = callback; status = wlan_platform_interrupt_connect(int_id, timer_isr, &timer_instance); wlan_platform_interrupt_enable(int_id); return status; } /*****************************************************************************/ /** * Starts the timer. Timer is always reset to its interval when started. First * timer interrupt will occur one interval after calling this function. * * @return None * *****************************************************************************/ void wlan_timer_start() { // Reset timer counter to the full interval value n every start // The first timer interrupt will occur one interval after the timer is started XTmrCtr_SetResetValue(&timer_instance, TIMER_NUMBER, timer_interval); XTmrCtr_Start(&timer_instance, TIMER_NUMBER); } /*****************************************************************************/ /** * Stops the timer * * @return WLAN_SUCCESS (cannot fail) * *****************************************************************************/ int wlan_timer_stop() { XTmrCtr_Stop(&timer_instance, TIMER_NUMBER); return WLAN_SUCCESS; } /*****************************************************************************/ /** * Timer interrupt handler * * @param instance_ptr - Pointer to the timer instance * * @return None * * @note This function is based on the timer's default ISR XTmrCtr_InterruptHandler * This version only checks one timer number and calls the framework's callback * directly, instead of via the timer config struct Handler field * *****************************************************************************/ void timer_isr(void* instance_ptr) { XTmrCtr* timer_ptr; u32 base_addr; u32 csr_reg; // Verify inputs are valid if (instance_ptr == NULL) { return; } timer_ptr = (XTmrCtr *)instance_ptr; base_addr = timer_ptr->BaseAddress; // Read Control / Status register csr_reg = XTmrCtr_ReadReg(base_addr, TIMER_NUMBER, XTC_TCSR_OFFSET); // Check if interrupt is enabled and interrupt is asserted if((csr_reg & XTC_CSR_ENABLE_INT_MASK) && (csr_reg & XTC_CSR_INT_OCCURED_MASK)) { // Increment statistics for the number of interrupts timer_ptr->Stats.Interrupts++; // Call the function set by the platform/framework timer_callback(); // Acknowledge the interrupt // The INT_OCCURED_MASK bit is self-clearing - code only needs to write 1 to clear the interrupt XTmrCtr_WriteReg(base_addr, TIMER_NUMBER, XTC_TCSR_OFFSET, (csr_reg | XTC_CSR_INT_OCCURED_MASK)); } return; } int timer_null_callback() { return 0; }